Scrapy爬取新浪微博用户粉丝数据

一般来说pc端的信息是最为全面的,但是防范措施也是最严格的。所以不能走weibo.com这个域名下进行爬取,新浪微博在pc端的反扒措施较为全面。而手机端的数据则相对好爬取,而且数据都是Json格式,解析起来十分方便。新浪微博的m端域名为m.weibo.cn。虽然是手机端,但是我们依然可以在电脑浏览器打开该网站,不需要连接手机设置手机网络代理什么的。

1. 确认爬取目标

本次爬取的目标用户为微博大V“回忆专用小马甲”。


image.png

我们可以看到该用户的粉丝数有接近四千万。

2.分析请求

点击博主的粉丝数进入粉丝列表,往下找到“他的全部粉丝”。


image.png

在这个页面我们就可以查看该博主的全部粉丝。
F12打开浏览器的开发者模式,选择“Network选项”,过滤器切换到XHR选项,然后在粉丝列表页面往下拉,就可以看到很多Ajax请求。


Ajax请求.png

框选出来的请求就是动态加载的Ajax请求。
再查看这些请求返回的数据,返回格式是Json,其中 card_group 字段就是一个个粉丝数据,一个请求共有20个粉丝数据。
响应数据.png

然后再分析比较不同的请求URL,发现URL中只有since_id 这个参数在改变。


image.png

image.png

我们只需向这个url发送请求,然后解析数据就可以爬取相关信息。

3.使用Scrapy框架爬取数据

我们首先要明确要爬取哪些数据。
分析请求返回的Json数据,确定我们爬取的数据为:
粉丝ID:fid
昵称:screen_name
头像:profile_image_url
主页:profile_url
粉丝数:followers_count
关注数:follow_count
简介:desc1


数据结构.png

虽然有gender字段,但是性别默认为null(男)。查看了几个粉丝信息后,发现不管男女这个字段的值始终为null,所以这里没有取用这个信息。用户的详细信息在其主页可以查看。


主页.png

3.1 items

确定好信息字段后就可以编写items.py

class SinaItem(scrapy.Item):
    # define the fields for your item here like:
    # name = scrapy.Field()
    fid = scrapy.Field()
    screen_name = scrapy.Field()
    profile_image_url = scrapy.Field()
    profile_url = scrapy.Field()
    followers_count = scrapy.Field()
    follow_count = scrapy.Field()
    desc1 = scrapy.Field()

3.2 spider/

import scrapy
import json
import jsonpath
from sina.items import SinaItem
class FansSpider(scrapy.Spider):
    name = 'fans'
    allowed_domains = ['m.weibo.cn']
    #  博主url
    url = "https://m.weibo.cn/api/container/getIndex?containerid=231051_-_fans_-_3217179555&since_id="
    offset = 1
    start_urls = [url+str(offset),]

    def parse(self, response):
        result = json.loads(response.text)
        cardgroup = jsonpath.jsonpath(result,"$..card_group")[0] # jsonpath返回一个列表
        for fan in cardgroup:
            # type(fan) --> dict
            item = SinaItem()
            item['fid'] = jsonpath.jsonpath(fan, "$..id")
            item['screen_name'] = jsonpath.jsonpath(fan, "$..screen_name")
            item['profile_image_url'] = jsonpath.jsonpath(fan, "$..profile_image_url")
            item['profile_url'] = jsonpath.jsonpath(fan, "$..profile_url")
            item['followers_count'] = jsonpath.jsonpath(fan, "$..followers_count")
            item['follow_count'] = jsonpath.jsonpath(fan, "$..follow_count")
            item['desc1'] = jsonpath.jsonpath(fan, "$..desc1")
            yield item
        self.offset += 1
        yield scrapy.Request(self.url + str(self.offset), callback=self.parse)

offset 为偏移量,解析完成之后再发送请求就可以完成循环爬取。

3.3 pipelines.py

管道文件有两个类,一个负责清洗,一个负责存入数据库。

class Qingxi(object):
    def process_item(self, item, spider):
        item['fid'] = item['fid'][0]
        item['screen_name'] = item['screen_name'][0]
        item['profile_image_url'] = item['profile_image_url'][0]
        item['profile_url'] = item['profile_url'][0]
        item['followers_count'] = item['followers_count'][0]
        item['follow_count'] = item['follow_count'][0]
        item['desc1'] = item['desc1'][0]
        return item

由于JsonPath返回的是一个列表,所以我们需要取出第一个元素,实际上也只有一个元素,不然不好存入数据库。

class SinaPipeline(object):
    def __init__(self, host, user, password, database,port):
        self.host = host
        self.user = user
        self.password = password
        self.database = database
        self.port = port

    def process_item(self, item, spider):
        sql = "insert into HuiYiZhuanYongXiaoMaJia (fid, screen_name, profile_image_url, profile_url, followers_count, follow_count, desc1) values (%s,%s,%s,%s,%s,%s,%s);"
        self.cursor.execute(sql, [item['fid'], item['screen_name'], item['profile_image_url'], item['profile_url'], item['followers_count'], item['follow_count'], item['desc1']])
        self.conn.commit()
        return item

    @classmethod
    def from_crawler(cls,crawler):
        #从settings.py 里获取配置信息
        return cls(
            host=crawler.settings.get('MYSQL_HOST'),
            user=crawler.settings.get('MYSQL_USER'),
            password=crawler.settings.get('MYSQL_PASSWORD'),
            database=crawler.settings.get('MYSQL_DATABASE'),
            port=crawler.settings.get('MYSQL_PORT')
        )

    def open_spider(self,spider):
        """
        当Spider开启时,这个方法被调用
        :param spider: Spider 的实例
        :return:
        """
        self.conn = pymysql.connect(
            host =self.host,
            user=self.user,
            password=self.password,
            database=self.database,
            port=self.port,
            charset='utf8'
        )
        self.cursor = self.conn.cursor()

    def close_spider(self, spider):
        """
        当Spider关闭时,这个方法被调用
        :param spider:
        :return:
        """
        self.cursor.close()
        self.conn.close()

3.4 middlewares.py

在刚开始没有设置User-Agent和下载延迟的时候,请求次数达到40多次后响应状态码变为了418:I am a teapot。特定搜索了一个418什么意思。

“客户端错误响应代码表示服务器拒绝冲泡咖啡,因为它是一个茶壶。 这个错误是超文本咖啡壶控制协议的参考,这是1998年愚人节的笑话。”
新浪还搞了这一手 o_o ....
这里下载中间件主要用来设置随机的User-Agent。

import random
class SinaDownloaderMiddleware(object):
    # Not all methods need to be defined. If a method is not defined,
    # scrapy acts as if the downloader middleware does not modify the
    # passed objects.
    def __init__(self):
        self.user_agents = [
            'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.140 Safari/537.36 Edge/17.17134',
            'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:6.0) Gecko/20100101 Firefox/6.0',
            'Opera/9.80 (Windows NT 6.1; U; en) Presto/2.8.131 Version/11.11',
            'Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; rv:11.0) like Gecko',
            'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; TencentTraveler 4.0)'
        ]

    def process_request(self, request, spider):
        request.headers['User-Agent'] = random.choice(self.user_agents)
        return None

3.5 最后在setting文件里开启相关配置即可。

数据库结果

如果我们要抓取粉丝的详细数据,比如性别,地址,学校,注册时间等可以再发送一个请求给粉丝的主页链接,而主页链接我们已经拿到了:profile_url,之后再写一个解析函数即可。

结论

我们发现最后爬取的结果只有四千多条。(简介有表情符号写不进数据库)
这是因为我们直接拿请求url在浏览器里测试:
https://m.weibo.cn/api/container/getIndex?containerid=231051_-fans-_2656274875&since_id=250,当测试到250页的时候会有返回数据。一页20条,总共5000条。

since_id=250.png

但是当测试到251页的时候,就没有数据了。
since_id=251.png

刚开始怀疑的是难道这个大V博主只有5000多粉丝,但是又觉得不太可能,毕竟粉丝数量显示的是3900多万。后来换了几个博主进行测试同样发现了这个问题,觉得是微博只会显示5000条数据。因为我们抓的是m.weibo.cn,也就是说,我们拿着手机在查看博主全部粉丝的时候如果一直往下滑一直往下滑,最多只会显示5000条数据。(这个结论是测试了央视新闻账号得出的,应该不只5000人关注了央视新闻吧)

PS最后我又测试了一下一个只有3000多粉丝的博主,粉丝结果居然只有5页,也就是100多个粉丝。但是首页显示的是3087,所以大概能得出这个博主也许可能是买的数据吧(因为我去买了2000粉丝,结果粉丝列表只有三个人)( ̄﹏ ̄;)

综上:本次爬取新浪微博用户粉丝数据没有什么卵用。
代码Github地址:https://github.com/wwxxee/Sina

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,547评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,399评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,428评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,599评论 1 274
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,612评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,577评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,941评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,603评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,852评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,605评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,693评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,375评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,955评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,936评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,172评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,970评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,414评论 2 342