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转换摩斯密码示例
Feb 16 Python
PyChar学习教程之自定义文件与代码模板详解
Jul 17 Python
Python实现对百度云的文件上传(实例讲解)
Oct 21 Python
Django 生成登陆验证码代码分享
Dec 12 Python
基于django ManyToMany 使用的注意事项详解
Aug 09 Python
python并发编程 Process对象的其他属性方法join方法详解
Aug 20 Python
python判断单向链表是否包括环,若包含则计算环入口的节点实例分析
Oct 23 Python
详解Python 循环嵌套
Jul 09 Python
python实现三壶谜题的示例详解
Nov 02 Python
python实现定时发送邮件到指定邮箱
Dec 23 Python
利用Python批量识别电子账单数据的方法
Feb 08 Python
Python爬虫爬取全球疫情数据并存储到mysql数据库的步骤
Mar 29 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
PHP5+UTF8多文件上传类
2008/10/17 PHP
PHP获取浏览器信息类和客户端地理位置的2个方法
2014/04/24 PHP
destoon网站转移服务器后搜索汉字出现乱码的解决方法
2014/06/21 PHP
Thinkphp框架中D方法与M方法的区别
2016/12/23 PHP
PHP实现根据数组的值进行分组的方法
2017/04/20 PHP
javascript 硬盘序列号+其它硬件信息
2008/12/23 Javascript
Javascript获取当前日期的农历日期代码
2014/10/08 Javascript
JS+CSS实现自动切换的网页滑动门菜单效果代码
2015/09/14 Javascript
详解JavaScript操作HTML DOM的基本方式
2015/10/21 Javascript
JS+CSS实现鼠标经过弹出一个DIV框完整实例(带缓冲动画渐变效果)
2016/03/25 Javascript
jquery分页插件jquery.pagination.js使用方法解析
2016/04/01 Javascript
js实现时间轴自动排列效果
2017/03/09 Javascript
Angular中$broadcast和$emit的使用方法详解
2017/05/22 Javascript
js 奇葩技巧之隐藏代码
2017/08/11 Javascript
详解vue2.0监听属性的使用心得及搭配计算属性的使用
2018/07/18 Javascript
利用原生JavaScript实现造日历轮子实例代码
2019/05/08 Javascript
vue 父组件通过v-model接收子组件的值的代码
2019/10/27 Javascript
JavaScript使用setTimeout实现倒计时效果
2021/02/19 Javascript
[02:34]DOTA2英雄基础教程 幽鬼
2014/01/02 DOTA
Python 爬虫图片简单实现
2017/06/01 Python
这可能是最好玩的python GUI入门实例(推荐)
2019/07/19 Python
PIP和conda 更换国内安装源的方法步骤
2020/09/21 Python
Django中如何用xlwt生成表格的方法步骤
2021/01/31 Python
解决img标签上下出现间隙的方法
2016/12/14 HTML / CSS
FLOS美国官网:意大利高级照明工艺的传奇
2018/08/07 全球购物
Halston Heritage官网:简洁的日装,稍显奢华的晚装
2018/11/20 全球购物
预备党员党课思想汇报
2014/01/13 职场文书
大学生求职自我评价
2014/01/16 职场文书
运动会广播稿400字
2014/01/25 职场文书
农民工工资承诺书范文
2014/03/31 职场文书
帮一个朋友写的求职信
2014/08/09 职场文书
新闻学专业职业生涯规划范文:我的人生我做主
2014/09/12 职场文书
解除劳动合同协议书
2014/09/17 职场文书
2015年党风廉政建设责任书
2015/01/29 职场文书
寒山寺导游词
2015/02/03 职场文书
承诺书应该怎么写?
2019/09/10 职场文书