Python分布式微博爬虫(源码分享)
时间:2022-05-07
本文章向大家介绍Python分布式微博爬虫(源码分享),主要内容包括关于本项目、你可以用它来干嘛、为何选择本项目、项目结构、配置和使用、基本概念、基础应用、原理机制和需要注意的事项等,并结合实例形式分析了其使用技巧,希望通过本文能帮助到大家理解应用这部分内容。
项目地址:https://github.com/ResolveWang/weibospider 作者:resolvewang
关于本项目
- 实现内容包括用户信息、用户主页所有微博、微博搜索、微博评论和微博转发关系抓取等
- 本项目基于本人实际的工作,并对此做了大量的修改和改进。希望能帮到对微博数据采集有需求的同学,对爬虫进阶 感兴趣的同学也可以看看。该项目从模拟登陆到各个页面的请求、从简单页面到复杂页面解析处理和相关的异常处理、 从单机到分布式迁移都做了大量的工作和反复测试,花了我绝大部分业余的时间
你可以用它来干嘛
- 微博舆情分析
- 论文撰写的一些数据,本项目会将抓到的所有数据不定时公布(资源和精力有限,暂时只有19.1w条微博用户数据)
- 自然语言处理的语料,比如热门微博的所有评论
- 爬虫进阶学习,对于需要学习Python进阶和爬虫的同学来说,都可以读读源码
为何选择本项目
- 功能全面:包括了用户信息抓取、指定关键字搜索结果增量抓取、指定用户主页所有微博抓取、评论抓取和转发关系抓取等
- 数据全面:PC端展现的数据量比移动端更加丰富。并且相比于其它同类项目对微博的简单分析,本项目做了大量细致的工作, 比如不同 domain 不同用户的解析策略、不同 domain 不同用户的主页分析策略等
- 稳定!项目可以长期稳定运行。
- 为了保证程序能长期稳定运行,数据所有的网络请求都是通过抓包手动分析的,未用任何自动化工具, 包括模拟登陆!从另一个方面来说,抓取速度也是比较有保证的(主要还是看账号数量)
- 通过合理的阈值设定,账号可以保证安全
- 即使账号不可用或者登陆失败,项目都对其做了处理(智能冻结账号,出错重试等),以保证每次请求都是有效的,并及时把错误反馈给用户
- 通过大量的异常检测和处理,几乎捕获了所有的解析和抓取异常。编写了大量的解析代码来获取足够全面的信息
- 复用性和二次开发性很好。项目很多地方都有详细的代码注释,方便阅读。即使本项目不能完全满足你 对微博数据采集和分析的需求,你也可以自己在该项目的基础上做二次开发,项目已经在微博抓取和各个 模版解析上做了大量工作。
- 由于本项目与本人实际工作有关联(代码并不是工作中使用的代码),所以可以放心它会长期更新。目前已经迭代一年有余了。
- 丰富文档支持:请点击wiki查看所有文档。如果文档仍然不能解决你的问题,推荐提issue,我们开发者看到后都会积极回答,也可以 通过加QQ群进行交流。注意在群里没事就潜水,有问题就问,开发者会积极回答。
# coding:utf-8
import time
from db import wb_data
from tasks.workers import app
from page_parse import comment
from config import conf
from page_get.basic import get_page
from db.weibo_comment import save_comments
# 起始请求地址
base_url = 'http://weibo.com/aj/v6/comment/big?ajwvr=6&id={}&page={}&__rnd={}'
@app.task(ignore_result=True)
def crawl_comment_by_page(mid, page_num):
cur_time = int(time.time() * 1000)
cur_url = base_url.format(mid, page_num, cur_time)
html = get_page(cur_url, user_verify=False)
comment_datas = comment.get_comment_list(html, mid)
save_comments(comment_datas)
if page_num == 1:
wb_data.set_weibo_comment_crawled(mid)
return html
@app.task(ignore_result=True)
def crawl_comment_page(mid):
limit = conf.get_max_comment_page() + 1
# 这里为了马上拿到返回结果,采用本地调用的方式
first_page = crawl_comment_by_page(mid, 1)
total_page = comment.get_total_page(first_page)
if total_page < limit:
limit = total_page + 1
for page_num in range(2, limit):
app.send_task('tasks.comment.crawl_comment_by_page', args=(mid, page_num), queue='comment_page_crawler',
routing_key='comment_page_info')
@app.task(ignore_result=True)
def excute_comment_task():
# 只解析了根评论,而未对根评论下的评论进行抓取,如果有需要的同学,可以适当做修改
weibo_datas = wb_data.get_weibo_comment_not_crawled()
for weibo_data in weibo_datas:
app.send_task('tasks.comment.crawl_comment_page', args=(weibo_data.weibo_id,), queue='comment_crawler',
routing_key='comment_info')
项目结构
- 功能模块
- 微博模拟登陆任务 login.py
- 微博用户抓取任务 user.py
- 微博特定话题搜索任务 search.py
- 微博用户主页信息抓取任务 home.py
- 微博评论抓取任务 comment.py
- 微博转发抓取任务 repost.py
配置和使用
- 环境配置:小白和新手请直接查看这里
- 考虑到Python3是趋势和一些将该项目用于学习的用户,项目运行环境为Python3.x
- 项目存储后端使用Mysql,所以需要在存储服务器上安装Mysql,注意设置字符集编码为utf-8
- 由于项目是使用celery做分布式任务调度,所以 需要使用broker和各个分布式节点通信,项目使用的是Redis,所以需要先安装Redis。 注意修改Redis的配置文件让它能监听除本机外的别的节点的请求,建议给Redis设置密码,如 果没设置密码,需要关闭保护模式(不推荐,这个有安全风险)才能和各个节点通信。如果害怕遇到Redis单点 故障,可以使用Redis主从配置。
入口文件:如果有同学有修改源码的需求,那么建议从入口文件开始阅读
- login.py和login_first.py:PC端微博登陆程序
- user.py和user_first.py:微博用户抓取程序
- search.py和search_first.py:微博话题搜索程序
- home.py和home_first.py:微博用户主页所有微博抓取程序
- comment.py和comment_first.py:微博评论抓取
- repost.py和repost_first.py:转发信息抓取
# coding:utf-8
import time
from logger.log import crawler
from tasks.workers import app
from page_parse.user import public
from page_get.basic import get_page
from db.wb_data import insert_weibo_datas
from db.seed_ids import get_home_ids
from config.conf import get_max_home_page
from page_parse.home import get_wbdata_fromweb, get_home_wbdata_byajax, get_total_page
# 只抓取原创微博
home_url = 'http://weibo.com/u/{}?is_ori=1&is_tag=0&profile_ftype=1&page={}'
ajax_url = 'http://weibo.com/p/aj/v6/mblog/mbloglist?ajwvr=6&domain={}&pagebar={}&is_ori=1&id={}{}&page={}'
'&pre_page={}&__rnd={}'
@app.task(ignore_result=True)
def crawl_ajax_page(url):
"""
返回值主要供第一次本地调用使用(获取总页数),网络调用忽略返回值
:param url:
:return:
"""
ajax_html = get_page(url, user_verify=False)
ajax_wbdatas = get_home_wbdata_byajax(ajax_html)
if not ajax_wbdatas:
return ''
insert_weibo_datas(ajax_wbdatas)
return ajax_html
@app.task(ignore_result=True)
def crawl_weibo_datas(uid):
limit = get_max_home_page()
cur_page = 1
while cur_page <= limit:
url = home_url.format(uid, cur_page)
html = get_page(url)
weibo_datas = get_wbdata_fromweb(html)
if not weibo_datas:
crawler.warning('用户id为{}的用户主页微博数据未采集成功,请检查原因'.format(uid))
return
insert_weibo_datas(weibo_datas)
domain = public.get_userdomain(html)
cur_time = int(time.time()*1000)
ajax_url_0 = ajax_url.format(domain, 0, domain, uid, cur_page, cur_page, cur_time)
ajax_url_1 = ajax_url.format(domain, 1, domain, uid, cur_page, cur_page, cur_time+100)
if cur_page == 1:
total_page = get_total_page(crawl_ajax_page(ajax_url_1))
if total_page < limit:
limit = total_page
cur_page += 1
app.send_task('tasks.home.crawl_ajax_page', args=(ajax_url_0,), queue='ajax_home_crawler',
routing_key='ajax_home_info')
app.send_task('tasks.home.crawl_ajax_page', args=(ajax_url_1,), queue='ajax_home_crawler',
routing_key='ajax_home_info')
@app.task
def excute_home_task():
# 这里的策略由自己指定,可以基于已有用户做主页抓取,也可以指定一些用户,我这里直接选的种子数据库中的uid
id_objs = get_home_ids()
for id_obj in id_objs:
app.send_task('tasks.home.crawl_weibo_datas', args=(id_obj.uid,), queue='home_crawler',
routing_key='home_info')
基本用法:请按照启动worker=>运行login_first.py=> 启动定时任务或者别的任务这个顺序进行
使用任务路由的最大好处是你可以为不同的节点分配不同的任务,这样让整个系统的数据采集效率更高。比如用户抓取,一个http请求只能得到一个用户信息,而对于用户关注和粉丝抓取,一个http请求可以得到几十个关注或者粉丝用户的uid,所以可以部署一个用户关注或者粉丝抓取节点,部署10个或者更多的用户信息抓取节点,让任务能比较均衡地执行。
找一个明星,试试爬取全部内容吧!
- 不给“爸爸”添麻烦 - iTOP iOS 动态库改造
- 移动SEO分享:php自动提交复合型Sitemap到百度搜索
- 《Android外部存储》
- Android JNI出坑指南
- 《iPhone X ARKit Face Tracking》
- 结合标签广告,定制一个QQ邮箱订阅
- SecureCRT全局发送相同命令,快速抓取服务器信息的方法
- [不定期更新]简单的shell脚本练习实例
- 超简单的MySQL主从复制配置步骤
- 解决Centos下vsftp无法上传文件的问题,附vsftp配置详解
- 为iFrame添加动态载入效果,提高用户体验
- 分享超炫的表白页面和爱的纪念日源码
- 分享WordPress Mobile Pack汉化精简版及隐藏指定插件更新提示的方法
- Tomcat重启脚本For Windows
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- Flutter 粘合剂CustomScrollView控件
- Flutter 1.17版本重磅发布
- Flutter 首页必用组件NestedScrollView
- 【Flutter实战】文本组件及五大案例
- 你真的会用Flutter日期类组件吗
- 【Flutter实战】图片组件及四大案例
- Flutter 标签类控件大全Chip
- 【Flutter实战】六大布局组件
- 【Flutter实战】定位装饰权重组件及柱状图案例
- Flutter为什么使用Dart?
- Flutter中如何使用WillPopScope
- 谈谈我对 Flutter 发展前景 和 “嵌套地狱” 的浅显看法
- 超过百万的StackOverflow Flutter 问题-第二期
- Flutter Flow实现半圆弹出菜单
- 【Flutter实战】自定义滚动条