使用 Python Scrapy 爬取微博内容【一】

来源:互联网 发布:看摄像头监控软件 编辑:程序博客网 时间:2024/05/16 06:56

2017.8.30更新:
全部工程代码上传百度盘。脚本现已停止开发。

工程代码:

链接:http://pan.baidu.com/s/1c1FWz76 密码:mu8k

————————————————————————————
开始正文前,我先阐述下我的选择的解决方案:Scrapy+beautifulsoup+Re+pymysql,爬取weibo手机版(反爬技术较少,比较容易)
Scrapy:爬虫框架,不多说
beautifulsoup:优秀的解析库,我用来解析lxml
Re:正则表达式
pymysql:MySql

思路简介

跳过繁琐的Scrapy 各个功能模块的介绍,我说下我的大致思路:

  1. 每个用户都会有自己的UID,通过对网页进行分析,我们可以获取到用户的UID,从而决定下一步的爬取方向。
  2. 微博每个用户的主页、粉丝与关注网页地址很有规律,得知了UID便可以很方便的完成对粉丝与关注列表等的爬取。
  3. 先通过正常登陆,得到COOKIES,让Scrapy使用该Cookies伪装用户。

对于分析获取的网页信息,我使用beautifulsoup与Re混合的方式(没系统学过网页,使用单一的beautifulsoup有点困难)。

Cookies的获取与分析

首先我们先正常登陆,来获取Cookies。
手机端的登陆我不知道为什么我无法点击登陆,所以我选择先正常登陆,然后再跳到手机版。
在手机版的抓包获取的Cookies如下:

这里写图片描述

根据名称,SUB即为代表身份的密钥。在实验中的结果也证明了服务器只对SUB,SUBP,SUHB这三个值敏感,其他的值服务器并不在意,但为了保险留下也是OK的。

之后我们需要替换Scrapy的User-Agent并禁止其遵守Robots.txt。

首先构造Header与Cookies(这个自己抓包就能获得):
这里写图片描述

同时,在settings.py中添加以下的User-Agent:

USER_AGENT = [    'Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US) AppleWebKit/534.14 (KHTML, like Gecko) Chrome/9.0.601.0 Safari/534.14',    'Mozilla/5.0 (Windows NT 6.0) AppleWebKit/535.2 (KHTML, like Gecko) Chrome/15.0.874.120 Safari/535.2',    'Mozilla/5.0 (Windows; U; Windows NT 6.0 x64; en-US; rv:1.9pre) Gecko/2008072421 Minefield/3.0.2pre',    'Mozilla/5.0 (Windows; U; Windows NT 6.0; en-GB; rv:1.9.0.11) Gecko/2009060215 Firefox/3.0.11 (.NET CLR 3.5.30729)',    'Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US; rv:1.9.1.6) Gecko/20091201 Firefox/3.5.6 GTB5',    'Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US) AppleWebKit/527 (KHTML, like Gecko, Safari/419.3) Arora/0.6 (Change: )',    'Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US) AppleWebKit/533.1 (KHTML, like Gecko) Maxthon/3.0.8.2 Safari/533.1 ']

之后我们需要设定其爬取的第一个网页:

def start_requests(self):        return [            Request('https://weibo.cn/' + '1234567890'+'/fans',cookies=Account_Cookies[0],meta=Header,callback=self.GetFollowers)            #Request('http://www.ipip.net/',callback=self.Show_IP)        ]

当其爬取到网页后,会调用 GetFollowers(self, response)
函数如下:

def GetFollowers(self, response):        Pipe_Item = self.Get_PipeItem(response,0)        self.Task_In_Queue = self.Task_In_Queue - 1        soup = bs4.BeautifulSoup(response.body.decode(response.encoding),'lxml')        Tag = soup.table        while Tag != None:            try:                if(str(Tag.a) != None):                    UID = re.search("/u/[0-9]{10}",str(Tag.a))#Re获取UID                    if UID != None:                        Pipe_Item['Datagram'].append(str(UID.group())[3:])                Tag = Tag.next_sibling            except Exception as e: #Not a Tag but a Navigate                Tag = Tag.next_sibling                continue        for rUID in Pipe_Item['Datagram']:            if rUID in self.Completed_UID:                continue            else:                if self.Task_In_Queue > Max_Waited_Length :                    break                else:                    self.Completed_UID.append(rUID)                    self.Task_In_Queue = self.Task_In_Queue + 1                    yield Request('https://weibo.cn/u/' + rUID ,cookies=Account_Cookies[0],meta=Header,callback=self.GetUsrInfo)                    yield Request('https://weibo.cn/' + rUID + '/fans',cookies=Account_Cookies[0],meta=Header,callback=self.GetFans)                    yield Request('https://weibo.cn/' + rUID + '/follow',cookies=Account_Cookies[0],meta=Header,callback=self.GetFollowers)        yield Pipe_Item

这个函数只获取了第一页的Follower,需要获取全部的可以自己再写一些。
最后循环中的3个yield,一次性提交全部需要爬取的网页,包括粉丝、关注、主页。
最后那个yield,提交Item给Pipeline处理。由于异步多线爬取,所以每次提交给Pipeline的Item,每个Item一定得带有分片序号,之后Pipeline才可以进行拼接。
代码本身很简单,不多说了。
下面是获取Item的函数:

def Get_PipeItem(self,response,Item_Type):        Pipe_Item = PipeItem()        Pipe_Item['Item_Type'] = Item_Type # 1 is get followers list        Pipe_Item['Datagram'] = []        Pipe_Item['Usr_ID'] = []        Pipe_Item['Usr_ID'] = str(re.search('[0-9]{10}',str(response.url)).group())        return Pipe_Item

首先将获取的网页传入,得到当前爬取用户的UID,并初始化Item其他属性。同样很简单。

下面是获取粉丝的函数:

def GetFans(self, response):        Pipe_Item = self.Get_PipeItem(response,1)        self.Task_In_Queue = self.Task_In_Queue - 1        soup = bs4.BeautifulSoup(response.body.decode(response.encoding),'lxml')        Tag = soup.table        while Tag != None:            try:                if(str(Tag.a) != None):                    UID = re.search("/u/[0-9]{10}",str(Tag.a))                    if UID != None:                        Pipe_Item['Datagram'].append(str(UID.group())[3:])                Tag = Tag.next_sibling            except Exception as e: #Not a Tag but a Navigate                Tag = Tag.next_sibling                continue        yield Pipe_Item

原理与获取关注一致,只是少了提交爬取申请这一步。

下面为获取用户信息的函数:

def GetUsrInfo(self, response):        Pipe_Item = self.Get_PipeItem(response,2)        self.Task_In_Queue = self.Task_In_Queue - 1        soup = bs4.BeautifulSoup(response.body.decode(response.encoding), 'lxml')        Info = soup.find('span', class_='ctt')        Pipe_Item['Datagram'].append(str(Info.text).replace(u'\xa0',u' ')[:-12])        Pipe_Item['Datagram'].append(re.search('关注\[\d+\]',str(soup.text)).group()[3:-1])        Pipe_Item['Datagram'].append( re.search('粉丝\[\d+\]',str(soup.text)).group()[3:-1])        Pipe_Item['Datagram'].append(re.search('微博\[\d+\]',str(soup.text)).group()[3:-1])        yield Pipe_Item

需要提一下的地方就是那个 str(Info.text).replace(u’\xa0’,u’ ‘),去除长空格,要不print测试的时候会有错误。
同样还是没有什么好说的。

还是很简单的。