基于PySpider的weibo.cn爬虫

作为科研狗,新浪微博一生黑。一开始打算花钱买他们的商业API,结果跟我说不跟科研机构合作,我也是日了狗了。后来费尽千辛万苦写了个爬虫,差点没把我小号封了手动再见.gif本来写字的阵地主要在lofter,结果lofter这坑货不支持代码高亮,让我这个码农如何自处?好了,闲话少叙已经叙了不少,把我这三天的奋斗结果稍稍记录一下。

一些学习资料

  1. Fiddler简易使用教程 抓cookies用(必看)
  2. PySpider简易教程 整个爬虫用到的框架(必看)
  3. HTTP Header入门详解 在模拟登录过程中要涉及到http头的设置,需要了解基本信息
  4. 全程模拟新浪微博登录2015 这个分析了新浪微博网页版现在还在用的登录过程,其中使用到的最新脚本ssologin.js版本号1.4.18。不过本文主要是基于wap版的爬虫,这个没必要看,如果想进一步爬取网页版的微博可以参考学习。
  5. Sina微博爬取@pyspider 这篇文章通过用户名和密码获取wap版微博的cookies后再进行后续操作,也可参考。
  6. PyQuery文档 && CSS选择器&&Python正则表达式re package文档
    Python页面爬下来以后用于操作页面元素获取需要的内容(必看)

获取cookies用于模拟登陆

参考学习资料1里面设置好fiddler,然后打开http://weibo.cn/ 没登陆的话登陆。在登录的状态下打开你要进行爬取的页面,观察fiddler里抓到的包,得到需要的cookies信息

基于PySpider的weibo.cn爬虫_第1张图片
获取cookies

对应设置好PySpider里的 crawl_config,需要注意的是 cookiesheaders,考虑到我的代码逻辑我把这两个都放在 crawl_config里而不是具体的爬取函数里。关于头的设置也可以参考fiddler里 Headers一栏,完整代码贴在最后可以参考我的设置。

爬虫逻辑

我的需求是对于指定的用户,给出用户主页(形如http://weibo.cn/kaikai0818http://weibo.cn/u/1788136742),爬取TA全部的微博及相关信息(如发布时间、转发数等)。

入口函数on_start没什么可说的,因为设置了全局的headers和cookies这边在self.crawl的时候就不需要再发送了,如果没设置成全局的,必须在这边设置并发送。

index_page函数主要用来计算一共有多少页需要爬取,页面有一个类型为hidden元素记录了一共有多少页,然后一个for得到所有需要爬取的页面地址。

list_single_page用于处理每一页微博的内容,一页有十条,主要就是操作页面获取单条微博的地址(因为后续需求可能需要爬取每一条微博的转发和评论内容)。这边有个trick,和web版微博不同的是,wap版单条微博并没有一个所谓的地址,只有一个转发或者评论的地址,代码里用了转发的地址。值得注意的是我自己测试这个爬虫的时候爬了1500条微博左右的时候账号被冻结了,建议在dashboard调一下rate、burst值。不过后来登录后激活了一下账号又能用了,还没测试过多少值可以避免被冻,因为不清楚新浪冻结的机制,我的小号还要用来花痴的,不敢瞎搞了(:з」∠)

detail_page用来处理单条微博,然后就是css选择器+正则的应用了哇,没什么可说的,就是找规律,目前测试过了没啥问题,不过爬下来的1500多条也没认真检查过,大家自己再看看了哇。

一个能跑的代码如下:

#!/usr/bin/env python
# -*- encoding: utf-8 -*-
# Created on 2016-03-07 13:26:00
# Project: user_timeline

import re
import time
from pyquery import PyQuery
from pyspider.libs.base_handler import *

class Handler(BaseHandler):
    user_url = "http://weibo.cn/kaikai0818"
    
    crawl_config = {
        'itag': 'v1',

        'headers': {
            'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:44.0) Gecko/20100101 Firefox/44.0',
            "Host": "weibo.cn",
            "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
            "Accept-Language": "zh-CN,zh-TW;q=0.8,zh-HK;q=0.6,en-US;q=0.4,en;q=0.2",
            "Accept-Encoding": "gzip, deflate",
            "DNT": "1",
            "Connection": "keep-alive"
        },

        'cookies': {
            "_T_WM":"I❤kkw(¯﹃¯)",
            "SUB":"I❤kkw(¯﹃¯)",
            "gsid_CTandWM":"I❤kkw(¯﹃¯)",
            "_T_WL":"1",
            "_WEIBO_UID":"I❤kkw(¯﹃¯)" 
        }
    }
    

    @every(minutes=24 * 60)
    def on_start(self):        
        self.crawl(Handler.user_url, callback=self.index_page,method="GET")   
    

    @config(age=1 * 24 * 60 * 60)        
    def index_page(self, response):
        #计算该用户的微博共有几页
        pages = response.doc("[type=hidden]").attr["value"]            
        for i in range(1,int(pages)+1):
            self.crawl(Handler.user_url+"?page="+str(i), callback=self.list_single_page,method="GET")
            
    
    @config(priority=2) #数字越大优先级越高
    def list_single_page(self, response):
        #处理当前页的微博        
        url = "http://weibo.cn/repost/"
        for each in response.doc("div[id^=\"M_\"]").items():             
            mid = each.attr("id")
            self.crawl(url + mid[2:], cookies = response.cookies, callback=self.detail_page) 
            #self.crawl可加上参数exetime=time.time() + 1*60 (即一分钟后再crawl)
        
            
    @config(priority=1)
    def detail_page(self, response):
        res = response.doc("#M_")
        
        #判断是否为转发
        if len(res.find("span.cmt")) != 0:
            is_rt = 1
        else:
            is_rt = 0
            
        #判断是否含图片
        if len(res.children("div").eq(1).find("img")) != 0:
            has_pic = 1
        else:
            has_pic = 0
        
        if is_rt == 0:
            text = res.find("span.ctt").text()
            orig_user = "NA"
            orig_text = "NA"
        else:
            orig_user = res("div:first-child span.cmt a").text()
            orig_text = res.find("span.ctt").text()            
            td = re.findall(r'([\s\S]+)

这辈子干得最痴汉的事情就是写了个爬虫把人一千多条微博扒拉下来了,感觉以后再也不会这么用力爱一个人了→_→
……
……
……
好了,我编不下去了,这就是科研狗的日常。doge.jpg doge.jpg

你可能感兴趣的:(基于PySpider的weibo.cn爬虫)