Skip to content

Latest commit

 

History

History
166 lines (111 loc) · 6.96 KB

1.md

File metadata and controls

166 lines (111 loc) · 6.96 KB

微博文案爬取

爬取微博的文案,你不可能一上来就把网上所有微博都爬下来。这就需要划定一个范围。比如说,某一账号下面所有的微博。

链接分析

https://weibo.com/ 这个地址据说比较难爬,会有反爬的措施,我直接放弃了。

https://m.weibo.cn/ 这个地址是手机端的,感觉还可以,就是文案好像只能爬2000条左右,后来我发现下面那个能爬好多,就不用了。

https://weibo.cn/ 这个是一个原始的3g版本微博,除了难看点,基本上可以满足爬取账号下所有微博的要求,当然,删掉了的肯定是没了。

目标选取

爬谁好呢。我打算研究一下正能量的官媒。人民日报、央视新闻、新华网、人民网、新华视点、新华社中国网事、中国之声。这几个影响力好像比较大。就先选人民日报吧。他的地址是这样的。

https://weibo.cn/rmrb 访问不了的话,好像是要登陆一下,反正不登录你待会儿也爬不了,你现在登陆一下自己的账号就可以了。

页面观察

看到这个页面,你会发现它还是比较原始的翻页的形式,不是那种下滑出现的懒加载。试着点击“下页”,看看地址栏的变化。它直接变成了这样。

https://weibo.cn/rmrb?page=2 很明显,这个地址后面只带一个参数,那就是第几页,然后点“上页”你就会发现首页其实是这样的。

https://weibo.cn/rmrb?page=1 所以,我们只要遍历page=1~n就可以了。那么n怎么获取呢。

首先你可以在顶上看到这个微博账号微博后面的方括号里有126595条微博,然后每页微博数是10条左右,那么大致上,n约等于12660。

但事实上,这个数量包含了已经被删除的微博,实际上在翻页的地方你可以看到,只有12658页。以这个为准就可以了。

那么怎么获取这个数呢。手动填写其实就可以满足了,自动的话,接下来对首页进行解析的时候可以顺带提一下。

函数构造

现在我们就来定义一个函数的框架,很简单就叫做get_contents

其实就涉及到两个变量,一个是微博账号名name,还有一个是要翻多少页page,为了在出现错误,爬取中断的时候可以续上,我们再引入一个开始爬取的页数start

def get_contents(name,page,start=1):
    print(name,page,start)

get_contents('rmrb',12658) # 这里start默认为1
get_contents('rmrb',12658,666)

然后,针对每一页,我们构造一个函数叫做get_contents_from_page

这里的变量其实就只有一个拼接好的地址url就可以了。

def get_contents_from_page(url):
    print(url)

get_contents_from_page('https://weibo.cn/rmrb?page=1')

于是我们可以把这两个函数拼接起来,形成我们完整的流程。

def get_contents(name,page,start=1):
    for current_page in range(start,page+1):
        print(current_page) # 为了追踪进度,出错重连,打印一下当前页码
        url = 'https://weibo.cn/%s?page=%d' % (name,current_page)
        get_contents_from_page(url)

页面请求

现在get_contents_from_page什么也没有做,只是打印了一下输入的地址,事实上,我们需要让他返回一个包含content_list的结果。

在前面的章节中,我们已经学习了使用requests模块进行页面请求,马上安排。

import requests

def get_contents_from_page(url):
    r = requests.get(url)
    print(r.text)

get_contents_from_page('https://weibo.cn/rmrb?page=1')

在这一步会遇到两个问题,一个是编码不对,出现乱码,还有一个就是要让你登陆,因为这个爬虫并没有用你的账号密码登陆过。

编码问题可以通过设置解决,而登陆问题需要你在https://weibo.cn/rmrb?page=1这个页面,打开F12,切换到Network面板,然后F5刷新,选择rmrb?page=1,然后查看它的Headers,在Request Headers里面找到cookie: XXX这一串。然后按这个格式填好。就可以获取正确的数据。

import requests

headers = {'cookie':'XXX'} # 按这个格式填好

def get_contents_from_page(url):
    r = requests.get(url,headers=headers) # 这里处理登陆问题
    r.encoding = 'utf-8' # 这里处理编码问题
    print(r.text)

get_contents_from_page('https://weibo.cn/rmrb?page=1')

页面解析

上面返回的数据是html格式的,我们需要通过解析器来对这个文档进行解析,在前面的章节中,我们学习过BeautifulSoup的使用,我们接下来就使用它对页面进行解析。而且根据对页面html的观察,所有的微博文案都在idM_开头的div中。

import requests
from bs4 import BeautifulSoup
import re # 用到一点正则匹配

headers = {'cookie':'XXX'}

def get_contents_from_page(url):
    r = requests.get(url,headers=headers)
    r.encoding = 'utf-8'
    soup = BeautifulSoup(r.text, 'html5lib') # 使用html5lib解析器,之前用的html.parser不太好

    # 在这里我们其实就可以获取总页数
    # info = soup.select('div.pa form div')[0].text
    # page = re.split(r"[\/'页']",info)[-2]
    # print(page)

    contents = soup.select("div[id^='M_']")
    for content in contents:
        content_manage(content)

get_contents_from_page('https://weibo.cn/rmrb?page=1')

数据清理

在上面的代码中,我们调用了一个content_manage函数,对每一个content进行处理,接下来,我们解析一些细节。

def content_manage(content):
    temp = {
        'bid': content['id'].split("_")[1],
        'content': content.find("span",class_="ctt").text,
        'attitudes_count': re.split(r"[\[\]]",content.find("a",string=re.compile(r"^赞\[")).text)[1],
        'reposts_count': re.split(r"[\[\]]",content.find("a",string=re.compile(r"^转发\[")).text)[1],
        'comments_count': re.split(r"[\[\]]",content.find("a",string=re.compile(r"^评论\[")).text)[1],
        'created_at': content.find("span",class_="ct").text
    }
    # print(temp) # 这个临时的文档结构就不打印了吧 直接给它存到数据库里吧
    save_content(temp)

数据储存

这个形式的数据直接存在MongoDB里会比较方便,在前面的章节中我们也学习过它的用法,于是可以这样。

import pymongo

myclient = pymongo.MongoClient("mongodb://XXX:[email protected]:27017/") # 登陆远程活本地的MongoDB
mydb = myclient["weibo_DB"] # 连接到存放微博的数据库db

def save_content(temp):
    mycol = mydb["weibo_contents"] # 存放到内容的集合collection
    flag = mycol.find_one({"bid":temp['bid']}) # 用一个简单的flag判断数据是否已经爬过了
    if flag:
        print('已存在') # 最终一次爬取可设为更新
    else:
        mycol.insert_one(temp)

如此一来,我们就不需要返回content_list了,登陆数据库可视化软件就可以非常直观的看到一列列的数据。