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编写类UNIX系统的命令行工具的教程
Apr 15 Python
Python中字典的基础知识归纳小结
Aug 19 Python
Python写一个基于MD5的文件监听程序
Mar 11 Python
python Django里CSRF 对应策略详解
Aug 05 Python
python求平均数、方差、中位数的例子
Aug 22 Python
python手写均值滤波
Feb 19 Python
Python异常继承关系和自定义异常实现代码实例
Feb 20 Python
python 子类调用父类的构造函数实例
Mar 12 Python
4行Python代码生成图像验证码(2种)
Apr 07 Python
解决Keras中CNN输入维度报错问题
Jun 29 Python
Pyinstaller打包Scrapy项目的实现步骤
Sep 22 Python
分享7个 Python 实战项目练习
Mar 03 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的一个登录的类 [推荐]
2007/03/16 PHP
phpMyAdmin下载、安装和使用入门教程
2007/05/31 PHP
PHP+javascript制作带提示的验证码源码分享
2014/05/28 PHP
WordPress的文章自动添加关键词及关键词的SEO优化
2016/03/01 PHP
php实现的二分查找算法示例
2017/06/20 PHP
thinkphp 抓取网站的内容并且保存到本地的实例详解
2017/08/25 PHP
PHP PDO和消息队列的个人理解与应用实例分析
2019/11/25 PHP
限制文本框输入N个字符的js代码
2010/05/13 Javascript
在浏览器中获取当前执行的脚本文件名的代码
2011/07/19 Javascript
AngularJS 日期格式化详解
2015/12/23 Javascript
node.js+express制作网页计算器
2016/01/17 Javascript
EasyUI闪屏EasyUI页面加载提示(原理+代码+效果图)
2016/02/21 Javascript
Base64(二进制)图片编码解析及在各种浏览器的兼容性处理
2017/02/09 Javascript
详解webpack自动生成html页面
2017/06/29 Javascript
jQuery实现的回车触发按钮事件功能示例
2018/03/25 jQuery
解决layer弹出层msg的文字不显示的问题
2019/09/11 Javascript
微信小程序左右滚动公告栏效果代码实例
2019/09/16 Javascript
使用Bootstrap做一个朝代历史表
2019/12/10 Javascript
[38:42]完美世界DOTA2联赛循环赛 Matador vs Forest BO2第二场 11.05
2020/11/05 DOTA
python常见的格式化输出小结
2016/12/15 Python
Python pandas DataFrame操作的实现代码
2019/06/21 Python
用python3 urllib破解有道翻译反爬虫机制详解
2019/08/14 Python
使用pth文件添加Python环境变量方式
2020/05/26 Python
Python3内置函数chr和ord实现进制转换
2020/06/05 Python
Django:使用filter的pk进行多值查询操作
2020/07/15 Python
解决pycharm导入numpy包的和使用时报错:RuntimeError: The current Numpy installation (‘D:\\python3.6\\lib\\site-packa的问题
2020/12/08 Python
在HTML5 canvas里用卷积核进行图像处理的方法
2018/05/02 HTML / CSS
使用 HTML5 Canvas 制作水波纹效果点击图片就会触发
2014/09/15 HTML / CSS
欧舒丹比利时官网:L’OCCITANE比利时
2017/04/25 全球购物
给老婆的婚前保证书
2014/02/01 职场文书
酒店端午节促销方案
2014/02/18 职场文书
科技之星事迹材料
2014/06/02 职场文书
给客户的检讨书
2014/12/21 职场文书
护士年终个人总结
2015/02/13 职场文书
Python实现简单的猜单词
2021/06/15 Python
Java移除无效括号的方法实现
2021/08/07 Java/Android