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 相关文章推荐
Python numpy生成矩阵、串联矩阵代码分享
Dec 04 Python
浅谈python脚本设置运行参数的方法
Dec 03 Python
Python GUI编程完整示例
Apr 04 Python
基于python if 判断选择结构的实例详解
May 06 Python
Python实现的对一个数进行因式分解操作示例
Jun 27 Python
python3 打印输出字典中特定的某个key的方法示例
Jul 06 Python
Flask框架钩子函数功能与用法分析
Aug 02 Python
Python Pandas 转换unix时间戳方式
Dec 07 Python
Python tempfile模块生成临时文件和临时目录
Sep 30 Python
Python基于mediainfo批量重命名图片文件
Dec 29 Python
python scrapy简单模拟登录的代码分析
Jul 21 Python
ROS系统将python包编译为可执行文件的简单步骤
Jul 25 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控制用户的浏览器--ob*函数的使用说明
2007/03/16 PHP
PHP+Mysql实现多关键字与多字段生成SQL语句的函数
2014/11/05 PHP
javascript之ESC(第二类混淆)
2007/05/06 Javascript
javascript在一段文字中的光标处插入其他文字
2007/08/26 Javascript
自己写了一个展开和收起的多更能型的js效果
2013/03/05 Javascript
左侧是表头的JS表格控件(自写,网上没有的)
2013/06/04 Javascript
jquery获得keycode的示例代码
2013/12/30 Javascript
Js操作树节点自动折叠展开的几种方法
2014/05/05 Javascript
浅谈重写window对象的方法
2014/12/29 Javascript
jQuery实现自定义checkbox和radio样式
2015/07/13 Javascript
体验jQuery和AngularJS的不同点及AngularJS的迷人之处
2016/02/02 Javascript
基于HTML模板和JSON数据的JavaScript交互(移动端)
2016/04/06 Javascript
jquery获取所有选中的checkbox实现代码
2016/05/26 Javascript
AngularJs 动态加载模块和依赖
2016/09/15 Javascript
基于vue.js实现图片轮播效果
2016/12/01 Javascript
bootstrap手风琴制作方法详解
2017/01/11 Javascript
vuex操作state对象的实例代码
2018/04/25 Javascript
React-router4路由监听的实现
2018/08/07 Javascript
小程序扫描普通链接二维码跳转小程序指定界面方法
2019/05/07 Javascript
javascript function(函数类型)使用与注意事项小结
2019/06/10 Javascript
jQuery实现图片随机切换、抽奖功能(实例代码)
2019/10/23 jQuery
vue中根据时间戳判断对应的时间(今天 昨天 前天)
2019/12/20 Javascript
JavaScript中reduce()的5个基本用法示例
2020/07/19 Javascript
详解python里使用正则表达式的全匹配功能
2017/10/19 Python
Python学习笔记之文件的读写操作实例分析
2019/08/07 Python
Python使用Excel将数据写入多个sheet
2020/05/16 Python
Django 实现 Websocket 广播、点对点发送消息的代码
2020/06/03 Python
python如何运行js语句
2020/09/09 Python
html5关于外链嵌入页面通信问题(postMessage解决跨域通信)
2020/07/20 HTML / CSS
建筑工程专业大学生求职信
2014/04/23 职场文书
四风问题个人对照检查材料
2014/09/26 职场文书
教师节联欢会主持词
2015/07/04 职场文书
学雷锋活动简报
2015/07/20 职场文书
python3实现常见的排序算法(示例代码)
2021/07/04 Python
TV动画《史上最强大魔王转生为村民A》番宣CM公布
2022/04/01 日漫
vue判断按钮是否可以点击
2022/04/09 Vue.js