python制作微博图片爬取工具


Posted in Python onJanuary 16, 2021

有小半个月没有发博客了,因为一直在研究python的GUI,买了一本书学习了一些基础,用我所学做了我的第一款GUI——微博图片爬取工具。本软件源代码已经放在了博客中,另外软件已经打包好上传到网盘中以供下载学习。

一.准备工作

本次要用到以下依赖库:re json os random tkinter threading requests PIL 其中后两个需要安装后使用

二.预览

1.启动

python制作微博图片爬取工具

2.运行中

python制作微博图片爬取工具

3.结果

这里只将拿一张图片作为展示。

python制作微博图片爬取工具

三.设计流程

设计流程分为总体设计和详细设计,这里我会使用viso画出几个流程图,用以展示我的思路,其中详细设计部分,我列举了两个函数实现的具体流程。

1.总体设计

python制作微博图片爬取工具

此图为整个系统的整体流程也是本GUI软件的使用过程。

2.详细设计

在此列举两个函数一个是搜索按钮触发的wb_search函数,一个是开始爬取按钮触发的wb_pics_parse函数。

2.1wb_search函数

python制作微博图片爬取工具

2.2wb_pics_parse函数

python制作微博图片爬取工具

四.源代码

import json
import random
import re
import os
from tkinter import *
from tkinter import messagebox
from tkinter import ttk
import requests
import threading
from PIL import Image,ImageTk

"""
1.07使用check button 实现下载完打开文件夹操作,注册了enter、esc热键,优化了一些体验
1.08 1.更新了关键字、磁盘、用户判断逻辑
   2.将之前的线程池改为多线程来执行下载操作
1.13说明:如果在下载过程变慢,可能是软件正在解析图片地址或者就是您的网络不行
"""
class WeiBo_pics_Spider(object):
  def __init__(self,start_url):
    self.start_url=start_url

  #解析出图片地址
  def get_pics_url(self):
    i = 1
    global a_flag
    a_flag = True
    while True:
      url = self.start_url + '&page={}'.format(i)
      headers = {'User-Agent': get_ua()}
      r = requests.get(url, headers=headers)
      _json = json.loads(r.text)
      items = _json["data"]["cards"]
      flag = _json['ok']
      if flag == 1 and a_flag: # 爬取数据标志+一个手动控制标志
        for v in items:
          picslist = v.get('mblog')
          if picslist is not None:
            img_urls = picslist.get('pics')
            if img_urls != None:
              for img_url_ in img_urls:
                img_url = img_url_['large']['url']
                yield img_url
      else:
        #1.06页数显示出现问题
        t1.insert(END, f'***在第{i}页终止***\n')
        t1.see(END)
        t1.update()
        if r1_var.get() == 1:
          big_dir=disk+':/WeiBo_Pics'
          os.startfile(big_dir)
        break
      i += 1
  #下载图片
  def download_pics(self,url,filename):
    headers={'User-Agent': get_ua()}
    r = requests.get(url, headers=headers)
    big_dir=disk+':/WeiBo_Pics'
    aim_path=big_dir+'/'+user_name_selected
    try:
      os.makedirs(aim_path)
    except:
      pass
    with open(aim_path + '\\' + filename, 'wb')as f:
      f.write(r.content)
      # 保证焦点始终在最下
      t1.see(END)
      # 下载完一张刷新一次 防止界面卡死崩溃
      t1.insert(END, f'{filename}\n')
      window.update()


def get_ua():
  first_num = random.randint(55, 62)
  third_num = random.randint(0, 3200)
  fourth_num = random.randint(0, 140)
  os_type = [
    '(Windows NT 6.1; WOW64)', '(Windows NT 10.0; WOW64)', '(X11; Linux x86_64)',
    '(Macintosh; Intel Mac OS X 10_12_6)'
  ]
  chrome_version = 'Chrome/{}.0.{}.{}'.format(first_num, third_num, fourth_num)

  ua = ' '.join(['Mozilla/5.0', random.choice(os_type), 'AppleWebKit/537.36',
          '(KHTML, like Gecko)', chrome_version, 'Safari/537.36']
         )
  return ua


def wb_search():
  #先清空lsibox1内容,便于新内容显示
  listb1.delete(0,END)
  url1='https://m.weibo.cn/api/container/getIndex?containerid=100103type%3D3%26q%3D{}%26t%3D0'
  headers={'User-Agent':get_ua()}
  key_word = e1.get()
  global user_id_list
  user_id_list=list()
  if len(key_word)!=0:
    #若用户输入了user_id,则去获取screen_name
    if re.match('\d{10}',key_word):
      user_id_list.append(key_word)
      url2 = f'https://m.weibo.cn/api/container/getIndex?uid={key_word}&containerid=100505{key_word}'
      r1 = requests.get(url2, headers=headers)
      _data = json.loads(r1.text)
      screen_name = _data['data']['userInfo'].get('screen_name')
      l3.place(x=120, y=42)
      l3_var.set(f'搜索成功')
      l3['background'] = 'green'
      listb1.insert(END, screen_name)
    #否则根据关键字去搜索用户信息,显示在listbox中
    else:
      aim_url=url1.format(key_word)
      r=requests.get(aim_url,headers=headers)
      _json=json.loads(r.text)
      try:
        #若出现了IndexError则表明没有检索到用户信息
        users=_json['data']['cards'][1].get('card_group')
        relevant_num=len(users)
        l3.place(x=105, y=42)
        l3_var.set(f'搜索到了 {relevant_num} 个用户')
        l3['background']='green'
        for user_ in users:
          user_info=user_.get('user')
          user_name=user_info.get('screen_name')
          id = user_info.get('id')
          """
          1.02的一种思路,使用一个列表存储screen_name和uid,两者用;(自定义字符,但应避免较少冲突)
          当获取Uid时,直接切割字符串,取Listbox所选项索引,按索引在列表表值(uid)
          #使用字符串拼接 格式:screen_name+';'+str(id)
          # user_data = user_name + ';' + str(id)
          """
          user_id_list.append(id)
          listb1.insert(END,user_name)
      except IndexError:#如果没有检索到用户,就会报列表索引错误
        messagebox.showinfo(title='提示', message='没有检索到相关用户,请更换关键字或使用用户id搜索!')
        l3.place(x=85, y=42)
        l3_var.set(f'请更换关键字或用户id搜索!')
        l3['background']='yellow'
        #没有检索到用户的话,提示之后,e1获得焦点之后,清除用户之前输入
        e1.bind('WM_TAKE_FOCUS', e1_clear())
  else:#处理没有输入关键字
    messagebox.showinfo(title='info',message='请输入关键字!')
    l3.place(x=110, y=42)
    l3_var.set(f'请输入关键字!')
    l3['background'] = 'red'

def wb_pics_parse():
  key_word=e1.get()
  select_path=c1.get()
  #1.先判断关键字是否输入
  if len(key_word)!=0:
    #2.再判断是否选择了磁盘
    if len(select_path)==1:
      #3.判断所选路径是否存在
      if not os.path.exists(select_path):
        #4.判断是否在列表框选择了用户名
        try:
          # 直接获取选中项目
          """1.05获取Listbox user_name_selected真费劲"""
          global user_name_selected
          user_name_selected=listb1.get(listb1.curselection())
          user_name_index = listb1.curselection()[0]
          user_id = user_id_list[user_name_index]
          container_id = '107603' + str(user_id)
          start_url = f'https://m.weibo.cn/api/container/getIndex?containerid={container_id}'
          spider = WeiBo_pics_Spider(start_url)
          t1.config(state='normal') # 将Text开启,置为可读可写状态
          l3.place(x=120, y=42)
          l3_var.set(f'正在运行......')
          l3['background'] = 'green'
          for pic_url in spider.get_pics_url():
            filename = pic_url.split('/')[-1]
            # 字符串切割,切割出前10个字符串
            filename = filename[10:]
            thread_it(spider.download_pics,pic_url,filename)

        #搜索后,但是没选择用户,会报TclError错误,此except就用来捕获这个异常
        except TclError:
          messagebox.showwarning(title='警告', message='请选择一个用户!')
          l3.place(x=105, y=42)
          l3_var.set(f'请选择一个用户!')
          l3['background'] = 'red'

        #获取当前选中项目(使用索引)
      else:
        messagebox.showwarning(title='警告',message='请检查路径!')
        l3.place(x=80, y=42)
        l3_var.set(f'请检查路径!')
        l3['background'] = 'red'
    else:
      messagebox.showwarning(title='警告', message='您未选择磁盘!')
      l3.place(x=85, y=42)
      l3_var.set(f'请检查是否选择了磁盘!')
      l3['background'] = 'red'
  else:
    messagebox.showwarning(title='警告', message='请输入关键字!')
    l3.place(x=110, y=42)
    l3_var.set(f'请输入关键字!')
    l3['background'] = 'red'

def open_disk():
  disk=c1.get()
  big_dir=disk+':/WeiBo_Pics'
  if len(disk)==1:
    try:
      if not os.path.exists(big_dir):
        os.mkdir(big_dir)
      os.startfile(big_dir)
    except:
      messagebox.showwarning(title='警告',message='选中的磁盘不存在!')
      l3.place(x=110, y=42)
      l3_var.set(f'选中的磁盘不存在!')
      l3['background'] = 'red'
  else:
    messagebox.showwarning(title='警告', message='您未选中磁盘!')
    l3.place(x=115, y=42)
    l3_var.set(f'您未选中磁盘!')
    l3['background'] = 'red'


def window_quit():
  ret=messagebox.askyesno(title='提示',message='是否要退出?')
  if ret==True:
    window.destroy()
    window.quit()


def e1_clear():
  e1.delete(0,END)

def print_path(event):
  #要使用完整的路径
  global disk
  disk = c1.get()
  disk_path=c1.get()+':/'
  if len(disk)==1:
    if os.path.exists(disk_path):
      messagebox.showinfo(title='提示',message=f'文件将存储到:{disk}:/WeiBo_Pics目录下')
    else:
      messagebox.showerror(title='错误',message='选定磁盘不存在!')
      l3.place(x=100, y=42)
      l3_var.set(f'选中的磁盘不存在!')
      l3['background'] = 'red'
  else:
    messagebox.showwarning(title='警告', message='请先选定磁盘!')
    l3.place(x=120, y=42)
    l3_var.set(f'请先选定磁盘!')
    l3['background'] = 'red'

def switch():
  if r1_var.get()==0:
    r1_var.set(1)
  else:
    r1_var.set(0)


def escape(event):
  window_quit()

def enter(event):
  wb_search()

'''解决程序卡死的重要方法,避免子线程和Ui线程在同一个线程'''
def thread_it(func, *args):
  '''将函数打包进线程'''
  # 创建
  t = threading.Thread(target=func, args=args)
  # 守护 !!!
  t.setDaemon(True)
  # 启动
  t.start()
  # 阻塞--卡死界面!
  # t.join()

window=Tk()
width=310
height=395
screenWidth = window.winfo_screenwidth() # 获取显示区域的宽度
screenHeight = window.winfo_screenheight() # 获取显示区域的高度
left = (screenWidth - width) / 2
top = (screenHeight - height) / 2
window.geometry("%dx%d+%d+%d" % (width, height, left, top))
window.resizable(0,0)
window.title('微博图片采集工具-v1.08')
#设置图标
ico_path=r'./rely/icon.ico'
window.iconbitmap(ico_path)
#插入图片到Label中
photo = Image.open("./rely/w_b.png") # 括号里为需要显示在图形化界面里的图片
photo = photo.resize((150, 40)) # 规定图片大小
img0 = ImageTk.PhotoImage(photo)
l1=ttk.Label(window,imag=img0,justify='center')
l1.pack()

l3_var=StringVar()
l3=ttk.Label(window,background='yellow',textvar=l3_var)
l3.place(x=120,y=42)
l3_var.set('还没搜索')


l1=ttk.Label(window,text='关键字或\n用户id:')
l1.place(x=13,y=60)

e1=ttk.Entry(window,justify='center')
e1.place(x=80,y=65)


l4=ttk.Label(window,text='磁盘:')
l4.place(x=13,y=100)

disk_list=['C','D','E','F','G','H','I']
c1=ttk.Combobox(window,justify='center',state='readonly',width=17,value=disk_list)
#Combobox默认选中索引为0的项目 即 C盘
c1.bind('<<ComboboxSelected>>', print_path)
c1.place(x=80,y=100)


r1_var=IntVar()
r1_var.set(1)#默认选中为1
check1=Checkbutton(window,text='下载完\n打开文件夹',command=switch)
check1.place(x=223,y=90)


b1=ttk.Button(window,text='搜索',command=lambda:thread_it(wb_search),width=7)
b1.place(x=230,y=63)

l5=ttk.Label(window,text='用户列表:')
l5.place(x=13,y=150)
lb1_var=StringVar()
listb1=Listbox(window,justify='center',listvariable=lb1_var,width=20,height=4)
listb1.place(x=80,y=135)

b2=ttk.Button(window,text='开始爬取',command=lambda :thread_it(wb_pics_parse,),width=7)
b2.place(x=230,y=160)

l6=ttk.Label(window,text='状态:')
l6.place(x=13,y=280)

t1=Text(window,width=23,font=('times new roman',10),state='disable')
t1.place(x=80,y=230,height=140)

b3=ttk.Button(window,text=' 打开\n文件夹',width=7,command=open_disk)
b3.place(x=230,y=230)

b3=ttk.Button(window,text='退出',width=7,command=window_quit)
b3.place(x=230,y=315)


f1 = ttk.LabelFrame(window)
f1.place(x=65,y=350)
l6=ttk.Label(f1,text='敬告:本软件仅供学习交流使用!',foreground='red')
l6.pack(anchor="w",fill=X)

#绑定esc键---退出
window.bind('<Escape>',escape)
#使用return键给输入框Entry绑定enter事件---search搜索
e1.bind('<Return>',enter)

#加入主窗口销毁事件
window.protocol('WM_DELETE_WINDOW',window_quit)
window.mainloop()

五.总结说明

本软件仅供学习交流使用!图源水印,在此仅作举例!
由于这是第一次做GUI,因此遇到了一些问题,在此列举一下:
1.窗口布局问题(GUI基础)
2.主窗口执行一个比较耗时操作导致卡死、崩溃(线程问题)。
3.主窗口关闭后,后台线程还在运行(线程问题)。

以上问题已经全部解决,软件切实可用。

另外,本软件有四大亮点:

1.使用线程下载图片
2.智能标签提醒
3.输入关键字直接敲回车能够完成搜索
4.Esc快速退出软件
软件打包好了放在了蓝奏云https://wws.lanzous.com/iPSpzkchj5i

以上就是python制作微博图片爬取工具的详细内容,更多关于python 微博图片爬取的资料请关注三水点靠木其它相关文章!

Python 相关文章推荐
wxpython中利用线程防止假死的实现方法
Aug 11 Python
Tornado协程在python2.7如何返回值(实现方法)
Jun 22 Python
Python三级菜单的实例
Sep 13 Python
python绘制双柱形图代码实例
Dec 14 Python
Python OpenCV获取视频的方法
Feb 28 Python
python利用多种方式来统计词频(单词个数)
May 27 Python
python实现在函数图像上添加文字和标注的方法
Jul 08 Python
TensorFlow基于MNIST数据集实现车牌识别(初步演示版)
Aug 05 Python
在pytorch 中计算精度、回归率、F1 score等指标的实例
Jan 18 Python
TensorFlow实现从txt文件读取数据
Feb 05 Python
推荐8款常用的Python GUI图形界面开发框架
Feb 23 Python
Python selenium自动化测试模型图解
Apr 15 Python
python工具——Mimesis的简单使用教程
Jan 16 #Python
Python 内存管理机制全面分析
Jan 16 #Python
python des,aes,rsa加解密的实现
Jan 16 #Python
python math模块的基本使用教程
Jan 16 #Python
详解Python模块化编程与装饰器
Jan 16 #Python
删除pycharm鼠标右键快捷键打开项目的操作
Jan 16 #Python
基于pycharm 项目和项目文件命名规则的介绍
Jan 15 #Python
You might like
PHP 字符串编码截取函数(兼容utf-8和gb2312)
2009/05/02 PHP
PHP中UNIX时间戳和日期间的转换与计算实例
2014/11/19 PHP
php新浪微博登录接口用法实例
2014/12/23 PHP
PHP环形链表实现方法示例
2017/09/15 PHP
php语言注释,单行注释和多行注释
2018/01/21 PHP
Laravel用户授权系统的使用方法示例
2018/09/16 PHP
php校验公钥是否可用的实例方法
2019/09/17 PHP
javascript实现轮显新闻标题链接
2007/08/13 Javascript
jQuery hover 延时器实现代码
2011/03/12 Javascript
让textarea自动调整大小的js代码
2011/04/12 Javascript
jquery 获取表单元素里面的值示例代码
2013/07/28 Javascript
Jquery时间轴特效(三种不同类型)
2015/11/02 Javascript
EasyUI Pagination 分页的两种做法小结
2016/07/09 Javascript
js实现微博发布小功能
2017/01/12 Javascript
node通过express搭建自己的服务器
2017/09/30 Javascript
angular4实现tab栏切换的方法示例
2017/10/21 Javascript
js合并两个数组生成合并后的key:value数组
2018/05/09 Javascript
JavaScript中常见内置函数用法示例
2018/05/14 Javascript
javascript创建元素和删除元素实例小结
2019/06/19 Javascript
使用Node.js在深度学习中做图片预处理的方法
2019/09/18 Javascript
javascript实现商品图片放大镜
2019/11/28 Javascript
vue 动态表单开发方法案例详解
2019/12/02 Javascript
Vue包大小优化的实现(从1.72M到94K)
2021/02/18 Vue.js
python的id()函数解密过程
2012/12/25 Python
Pytorch to(device)用法
2020/01/08 Python
python+django+selenium搭建简易自动化测试
2020/08/19 Python
Django修改app名称和数据表迁移方案实现
2020/09/17 Python
法国创作个性化T恤衫和其他定制产品平台:Tostadora
2018/04/08 全球购物
英语教师岗位职责
2014/03/16 职场文书
演讲稿格式
2014/04/30 职场文书
2016中秋节广告语
2016/01/28 职场文书
学习心得体会
2019/06/20 职场文书
公司员工违法违章行为检讨书
2019/06/24 职场文书
python实现简单的井字棋
2021/05/26 Python
Redis基于Bitmap实现用户签到功能
2021/06/20 Redis
bose降噪耳机音能消除人声吗
2022/04/19 数码科技