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编写的com组件发生R6034错误的原因与解决办法
Apr 01 Python
Python3指定路径寻找符合匹配模式文件
May 22 Python
pandas数据预处理之dataframe的groupby操作方法
Apr 13 Python
python制作mysql数据迁移脚本
Jan 01 Python
Python父目录、子目录的相互调用方法
Feb 16 Python
Python 文件操作之读取文件(read),文件指针与写入文件(write),文件打开方式示例
Sep 29 Python
python自动化实现登录获取图片验证码功能
Nov 20 Python
PyQt使用QPropertyAnimation开发简单动画
Apr 02 Python
500行python代码实现飞机大战
Apr 24 Python
python实现批量转换图片为黑白
Jun 16 Python
python温度转换华氏温度实现代码
Dec 06 Python
学点简单的Django之第一个Django程序的实现
Feb 24 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实现将HTML5中Canvas图像保存到服务器的方法
2014/11/28 PHP
Javascript调试工具(下载)
2007/01/09 Javascript
IE8下关于querySelectorAll()的问题
2010/05/13 Javascript
jquery中的mouseleave和mouseout的区别 模仿下拉框效果
2012/02/07 Javascript
jquery动态改变div宽度和高度
2015/02/09 Javascript
js如何实现淡入淡出效果
2020/11/18 Javascript
全屏js头像上传插件源码高清版
2016/03/29 Javascript
JS数组操作(数组增加、删除、翻转、转字符串、取索引、截取(切片)slice、剪接splice、数组合并)
2016/05/20 Javascript
实例解析jQuery中proxy()函数的用法
2016/05/24 Javascript
js复制内容到剪贴板代码,js复制代码的简单实例
2016/10/27 Javascript
BootStrap整体框架之基础布局组件
2016/12/15 Javascript
详解Vue用axios发送post请求自动set cookie
2017/05/10 Javascript
JS库之ParticlesJS使用简介
2017/09/12 Javascript
jQuery自动或手动图片切换效果
2017/10/11 jQuery
js通过Date对象实现倒计时动画效果
2017/10/27 Javascript
js中apply与call简单用法详解
2017/11/06 Javascript
vue中引用阿里字体图标的方法
2018/02/10 Javascript
JavaScript 正则命名分组【推荐】
2018/06/07 Javascript
python实用代码片段收集贴
2015/06/03 Python
Python 异常处理的实例详解
2017/09/11 Python
pygame游戏之旅 调用按钮实现游戏开始功能
2018/11/21 Python
python实现连续图文识别
2018/12/18 Python
ffmpeg+Python实现B站MP4格式音频与视频的合并示例代码
2020/10/21 Python
Levi’s美国官网:美国著名的牛仔裤品牌
2016/08/19 全球购物
Merrell美国官网:美国登山运动鞋品牌
2018/02/07 全球购物
Grow Gorgeous美国官网:只要八天,体验唤醒毛囊后新生的茂密秀发
2018/06/04 全球购物
印度手工编织服装和家居用品商店:Fabindi
2019/10/07 全球购物
Emma Bridgewater官网:英国餐具制造商
2019/11/24 全球购物
Eton丹麦官网:精美的男式衬衫
2020/05/27 全球购物
普通员工辞职信
2014/01/17 职场文书
中专毕业生个人职业生涯规划
2014/02/19 职场文书
工商治理实习生的自我评价分享
2014/02/20 职场文书
优秀工会工作者事迹材料
2014/06/02 职场文书
销售员态度差检讨书
2014/10/26 职场文书
工作检讨书怎么写
2015/01/23 职场文书
详解Python自动化之文件自动化处理
2021/06/21 Python