微博爬虫

Posted by Daryl on May 7, 2018

既不鲁棒又不高效的辣鸡爬虫

微博爬虫

最近的任务需要研究对话中的post-response pair,虽然之前也找了一些数据,但是对于现在的需求还是太少了。然后两三周前得知idea被隔壁的童鞋抢发的消息,就要来了paper看了一下,从中获取灵感决定从微博上爬一点数据下来(你的思路到底有多狭窄…)。好啦我也承认之前不敢爬微博是因为它反爬虫反的太厉害了QAQ。。。

不过最后还是鼓起勇气去爬了微博…因为这种全是动态的网页还是第一次搞,查了一下资料发现可以根据其ajax来爬取信息。基本上就是你给微博的服务器发一个request,然后它就给你返回一个json信息,根据这个json信息你就可以知道这条微博的内容是什么,回帖有哪些。同时为了处理的简单,选择了移动版的网页爬取。(一般来说移动版的数据都比pc版的好爬)

具体步骤是这样:先选择一个爬虫起点uid(每个微博用户用一个唯一的uid标识),然后用https://m.weibo.cn/api/container/getIndex?这个url构造request(具体情况还是去看代码),这里我爬了其前10页的微博。解析器返回的json,发现里面有个cards,cards下面有每一条微博的具体地址(即每条微博的单独标识id),再用https://m.weibo.cn/api/comments/show?id=这个url构造request即可得到每个微博下面的评论。把第一条评论内容找出来,和微博内容构成一个 post-response pair,就大功告成了。然后把每个评论的人的id加入待爬id池,就可以开心的一直循环了~

然后谈谈问题:
第一个无疑就是封ip了,封了ip这个程序就直接死掉了。解决办法也不是没有,一个是可以sleep几秒再爬,一个是去找免费ip代理。但是这样都太慢了,现在爬微博本身就很慢,再处理这个东西就更慢了。
第二个是会有个json解析错误。现在不知道具体是为什么,而且我明明都加了try语句的,也不知道为什么会有错,还是再看看吧。

附上代码:

import time
from urllib.parse import urlencode

import requests
# from pyquery import PyQuery as pq

start_id = '1904769205'

crawl_list = [start_id]
crawl_index = 0
crawl_set = set()
crawl_set.add(start_id)

all_pr_pair = []


base_url = 'https://m.weibo.cn/api/container/getIndex?'

headers = {
    'Host': 'm.weibo.cn',
    'Referer': 'https://m.weibo.cn/u/1904769205',
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.139 Safari/537.36',
    'X-Requested-With': 'XMLHttpRequest',
}


def get_page(page, val):
    params = {
        'type': 'uid',
        'value': val,
        'containerid': '107603' + val,
        'page': page
    }
    url = base_url + urlencode(params)
    try:
        response = requests.get(url, headers=headers)
        if response.status_code == 200:
            return response.json()
    except requests.ConnectionError as e:
        print('Error', e.args)


def my_parse_json(json):
    if json:
        pr_pair = []
        try:
            items = json.get('data').get('cards')
        except Exception as e:
            print(e)
            print('the json has no crads')
            return pr_pair
        for item in items:
            try:
                mblog = item.get('mblog')
                post = mblog.get('text')

                # bid = mblog.get('bid')
                rid = mblog.get('id')

                # new_header = {
                #     'Host': 'm.weibo.cn',
                #     'Referer': 'https://m.weibo.cn/u/' + bid,
                #     'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.139 Safari/537.36',
                #     'X-Requested-With': 'XMLHttpRequest',
                # }
                new_url = 'https://m.weibo.cn/api/comments/show?id=' + rid + '&page=1'

                # new_res = requests.get(new_url, headers=new_header)
                new_res = requests.get(new_url)
                all_data = new_res.json().get('data').get('data')
                # print("ddd")

                response = all_data[0].get('text')
                pr_pair.append((post, response))

                for data in all_data:
                    usr_id = data.get('user').get('id')
                    usr_id = str(usr_id)
                    if usr_id in crawl_set:
                        pass
                    else:
                        crawl_set.add(usr_id)
                        crawl_list.append(usr_id)
                    # print(str(usr_id))
                    # input()

            except Exception as e:
                # print(e)
                pass
        return pr_pair


# json = get_page(1, '2830678474')
# pr = my_parse_json(json)

fout = open("res.txt", 'w', encoding='utf-8')
start = time.time()
total_len = 0

while crawl_index < len(crawl_list) and crawl_index < 100000:
    # for every one, at most crawl 10 pages
    for i in range(1, 11):
        json = get_page(i, crawl_list[crawl_index])
        pr = my_parse_json(json)
        # print(len(pr))
        try:
            for pair in pr:
                fout.write(pair[0] + '\n')
                fout.write(pair[1] + '\n')
                fout.write('\n')
            # all_pr_pair.extend(pr)
            total_len += len(pr)
        except Exception as e:
            pass
    crawl_index += 1

    if crawl_index % 1000 == 0:
        end = time.time()
        print(crawl_index, total_len, end - start)
    # print(crawl_index, len(all_pr_pair))

fout.close()