代码下载


所有代理部分都可以不管,淘宝有整个电脑的ip切换,因为项目用到了selenium、scrapy、session、requests多种方法,加代理不容易,淘宝动态ip,他们都是可以多少秒一切换电脑ip的。


上面是整个项目代码的压缩包,weixinspider下主要是对微信的爬取,分为自动爬取和关键字爬取,用的是scrapy,都没有用代理,就在爬取页有个简单的系统代理,这个看以后用什么代理需不需要代理池再改吧,压缩包里有个代理池文件(ProxyPool),目前是没有使用代理池,所以不需要。然后就是对微博(weibospider)进行爬取,同样也分为自动(autofind)和关键字(findbykeyword),其中关键字爬取分为深度爬取(deepspider)和快速爬取(rapidspider),快速爬取依然采用Selenium,深度爬取才去Splash,目前看每小时爬取评论应该在10w左右。
这里我们首先考虑的是微博的动态加载,动态加载中间自然也就可能包括很多加密参数等,这就对爬虫的编写造成了很大的阻碍,也算是一种比较经典的反爬手段。之前我是用的Selenium动态渲染页面爬取,效率实在低的可怕,很多地方需要等待,但聊胜于无,还是写完了并且可以执行。现在使用的Scrapy-Splash 来渲染,效率会有所上升(原因后面会提到)。
但是使用Scrapy-Splash 需要Docker来拉取镜像,Docker可以理解成一个微型虚拟机,它可以把需要的环境放上去,其它机器需要环境直接拉取就行,不需要再次配置,一般在分布式中使用的比较多,只要在Docker上搞好环境,别的分布式服务器只要拉取,就具备相应的所有环境。
Windows 下 Docker的下载。
 
然后我们拉取splash镜像,
docker pull scrapinghub/splash
等待拉取完成,效果如下图

然后启动splash(会启动8050端口,注意端口占用)
docker run -p 8050:8050 scrapinghub/splash
效果如下图

其他的比如BeautifulSoup、re等这些爬虫必备的包就不再这里累赘,更多的点这里
到这里基本环境就准备完成了。
 
然后就是写爬虫最重要的,甚至比代码都重要,前期对参数、url、html、json的分析,这部分才是爬虫最核心的部分(当然是我认为的ww)
首先我们分析发送到客户端的json文件,我们过滤XHR(XmlHttpRequest)请求,我们发现其中一个的响应报文和我们的显示消息很像。
我们对热门微博下的html进行分析,我们发现都在action-type 为feed_list_item下,我们再对每个微博点击评论。

 
 
评论部分就是动态加载的,所以我们在这需要分析他的动态响应报文。

,我们将这段代码复制到IDE,并整理一下格式,发现的却能找到下面评论中的评论,点赞数,发布时间等(因为上图中前几个人均没有点赞,我找的最后一个人的评论截的图)

同时我们在json最后找到了一个不知道是什么的链接,结合后面文本,我们分析是更多评论的链接,复制到浏览器发现果然,是该微博的详细页面,同时 XHR中发现关键的json请求。

我们同样将响应报文复制到IDE然后整理格式,发现了我们需要的信息。

对于二级评论(评论下的评论),全部在list_box_in_$_bg3 这个class中,我们不需要二级评论,所以可以将这整个标签跳过。

在最后我们同样得到了不知道什么的链接,结合后面的文本信息,我们分析可能是下一趟加载的链接,这只是个相对链接,那么绝对链接是什么样子的呢?

我们多下拉几次评论,使他多请求几次json(注意我过滤了,因为有其他干扰请求,为了方便截图我给过滤了),我们发现  所有的返回评论信息的json,他们的请求网址都是类似这个样子,前面都是一样的,后面呢和最后的url格式一致,所以判断这个的确就是后面评论的url
复制到浏览器验证,发现正确。

 
到这我们基本的爬虫思路就完成了, 只要在搜索框中搜索定义的key_word,根据key_word生成首次访问的url,然后对返回的html正文进行筛选,找出评论页(即详情页,该页需要被渲染,对于微博全文也在该页中抓取),然后在详细页中抓取其他页评论,因为首页渲染后的代码中包括后页URL,所以后页不需要被渲染,只需要按照url找下去即可,就加快了速度,实际上Splash我感觉速度甚至都没有Selenium快,至少感觉不出来,因为Splash是一个强制等待时间,10秒就是10秒,好了也要等,这就造成了一定时间的浪费。看了下官方文档,只有放弃时间的timeout和等待时间的wait,并没有什么好了就好了那种,这里点名批评Splash(
然后就是对代码的讲解。
首先是main这里。

if __name__ == '__main__':
    # from sys import argv
    #
    # script, keyword = argv
    # 构造请求头
    agent = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36',
    cookies = 'UOR=blog.csdn.net,widget.weibo.com,login.sina.com.cn; SINAGLOBAL=3755037512343.2812.1519369021129; ULV=1537357671429:10:3:2:5950190259993.881.1537357671426:1537344955743; SCF=AqsaV9LwZxR9PQcOz2arJcpSAiVoqhY6xsWh5IRYKMIG2lC2V45Wdcz-2GcZMBPxH2L4p6ce-tPitn4vaB81ys8.; SUHB=0y0YPZ53A4O6ze; YF-V5-G0=a53c7b4a43414d07adb73f0238a7972e; _s_tentry=-; Apache=5950190259993.881.1537357671426; YF-Page-G0=cf25a00b541269674d0feadd72dce35f; YF-Ugrow-G0=57484c7c1ded49566c905773d5d00f82; login_sid_t=3b1b25881b10828ce0b756dce6a5b36e; cross_origin_proto=SSL; ALF=1569028913; SSOLoginState=1537406188; un=18742037051; wvr=6; wb_view_log_2656260571=1920*10801; SUB=_2A252oDfjDeRhGeRI7lQT9i7JzD2IHXVV1C4rrDV8PUNbmtAKLRKkkW9NUrGLcTC190OV6nVQ-V6YdlKRwIYiSsu7; SUBP=0033WrSXqPxfM725Ws9jqgMF55529P9D9W5ELXNzeq6ma_-JYkQSczuJ5JpX5KMhUgL.FozcSKqESo5fS022dJLoIN-LxK.L1KeL1h2LxKqL1-qLBo2LxKqL1-eLB-2LxKqL1-BLBK-LxKMLBKML1K2LxKqL122L1h5LxKqL1-eL1h.LxK.L1K.L1K5LxK-L1h-LB-eLxK-L12qLBonLxKnL1K2LBKeLxKBLB.2L12zLxK.L1K-LB.qt'
    headers = {
        'User-Agent': agent,
        'Cookie': cookies
    }
    # 新建会话
    session = requests.session()
    # 爬取前n-1页
    for i in range(1, 21):
        collect('林俊杰', session, cookies, int(i))

生成headers,这里必须要加cookie,同时应该注意对cookie的更新,这个用自己的就行,从浏览器上复制下来,一般一两天就过期了吧可能,需要重新复制。
agent不需要更新。
然后构造了headers,生成session会话,就可以用这个session遍历。
上面注释掉的是调用传参,类似与java里的args,暂时没用到,就先注释掉。
 
然后就是核心的collect方法:(我上传到服务器的是我没修改过的版本,各种输出、注释、空白行还都在,为了保证我下面说的行数一致,就先别删,看明白了再删)

这里首先是对关键字编码,因为url中不允许出现中文,所以对关键字进行编码。
然后就是构建URL,这个URL就是搜索页面。
然后这里我们调用了一个代理,先不管那个方法,总之是返回了一个代理ip,然后访问url,获取到返回。因为涉及到中文,所以需要使用unicode进行解码,同时我们发现返回的是十六位编码,所以使用unicode_escape 来进行解码,同时替换掉其他字符。
比如,我们在添加断点之后,比较get_value1(临时添加,代码里没有) 和get_value 的值,发现一个是十六位十六进制编码,一个是中文编码。

然后用bs4解析,解析到每个微博的标签,开始对每个微博进行遍历。
for中就是对每条微博的遍历,首先就是获取各种信息,注意在获取发送方式的地方,有一个处理,事实上这种处理在以后的地方还很多,有一个地方为了保证程序的完整性甚至加了多层try,在上传的代码中并没有加,后面会提到。

在62行这里,调用的Splash渲染,也是一趟爬取中唯一的一次渲染。
这里后面使用代理部分我给注释掉了,事实证明完全可以,只要在得到微博页面的时候使用代理就可以了。
这里刚开始就是注释掉了拿新代理,这里不用代理也行,之后就一样,bs4解析,获取正文,这里80行之后,是新增的,是因为这个context总获取不到,报错commen是NoneType,没有find方法,分析可能是get的问题(据大佬说是被服务器重定向了,这需要对network仔细分析  [ 其实也可能是因为我调试的时候为了加快速度总是同时开四五个进程,可以试试只单开一个会不会发生这个问题]    2018/10/7更新 这个问题应该是splash不能多线程渲染导致的。。可以做个队列渲染 或者一个搞。。),之前没太在意,因为爬虫会遇到很多不可知的错误,比如之前微博在评论中放广告导致爬评论总抛异常(后面会说到),我研究了好久才发现这个问题。所以在这就是再try一次,如果再失败就放弃这条微博。

也算有点效果,至少连续性保证了,至于问题是什么,始终不知道,url解析的也是正确的emm

下面就是获取mid参数,这个mid是微博的主键,每个微博唯一,需要这个mid去访问详细页面。一些异常处理在注释中已经说明,这里对两次获取不到mid的微博进行了日志输出,上面那个之所以没输出是临时起意,其实我感觉输出了也没啥用,那么多error总不能人工重新爬取吧..前提是已知url是对的。

然后这里就是由mid拼接url,148行的pinglun(这个命名emmm,是个轮子,就懒得改名了)是另外的方法,先不说,总是是返回了n条评论构成的字典。

然后就是进循环了(因为第一次需要单独解析,以后的url在前一条里,所以除了第一次,其其他的进循环),没什么好说的,值得一提的就是最后一点,因为拉取评论的时候,有的时候是下拉自动刷新,有的时候需要点击更多,所以在循环的最后需要考虑到这两种情况,finally中,构造数据结构并调用保存方法保存到数据库中。

 
到这就算是爬取完成了,再就是其他我们用到过的调用。
这个是保存到数据库中的方法,注释比较多,不做过多解释。

 
然后是时间处理和获取代理的方法,时间处理本意上是处理例如“1分钟前”、“1小时前”这类数据,将其转换成具体的时间,但是没有用到(微博的快速查找和自动查找都用了这个方法),放在这留作被用。
代理呢,就是代理商提供的各种信息,最后拼接成一个代理返回。

 
pinglun方法呢,就是对评论进行处理,然后构造成字典返回,这里在25行重新使用了pyquery。。因为我实在是用bs4解析不出来了,这里换选择器解析。

 


1 条评论

Buyringash · 2020-07-13 22:15

Unknown Unknown Unknown Unknown

无敌

发表回复

Avatar placeholder

您的电子邮箱地址不会被公开。 必填项已用 * 标注