scrapy 爬取果壳问答

该文介绍自己爬取果壳精彩问答的内容

1、创建项目:scrapy startproject GuoKr

2、进入GuoKr目录下,以crawl 模板创建爬虫:scrapy genspider -t crawl guokr guokr.com

3、在item.py中定义爬取的数据的属性:问题、回答以及问答的链接

# 果壳问答中的问题属性question
    question = scrapy.Field()
    # 果壳问答中的回答属性answers
    answers = scrapy.Field()
    # 果壳问答中的链接属性
    url = scrapy.Field()

4、爬取规则和页面处理 spider\guokr.py

(1)爬取的入口链接是https://www.guokr.com/ask/highlight/?page=1,精彩问答的问答列表是根据翻页进行展示的,所以要先爬取问答列表页,获取翻页的链接,根据列表页再爬取问答的详情页链接,根据详情页链接爬取详情页的数据,在详情页中解析需要的数据。
在crawl模版中,有rules规则,可以根据Rule()函数来对翻页和问答链接进行提取:
spider.guokr.py

rules = (
        # 关于列表页的链接的提取,以跟进的方式提取到所有的列表页
        Rule(LinkExtractor(allow=r'page=\d+'), follow=True),
        # 关于问答详情页面链接的提取,不跟进详情页页面的链接提取,爬取到详情页面后,交与parse_item()函数处理
        Rule(LinkExtractor(allow='question'), callback="parse_item", follow=False),
    )

Rule()类中可以决定要爬取的链接匹配LinkExtractor,爬取链接后的回调函数callback以及是否要跟踪链接的提取。

Rule(self, link_extractor, callback=None, cb_kwargs=None, follow=None, process_links=None, process_request=identity)

LinkExtractor类主要用于当前页面链接的过滤

LinkExtractor(self, allow=(), deny=(), allow_domains=(), deny_domains=(), restrict_xpaths=(), tags=('a', 'area'), attrs=('href',), canonicalize=False, unique=True, process_value=None, deny_extensions=None, restrict_css=(), strip=True)

allow字段是直接用的正则表达式进行匹配,符合要求的链接留下;deny则是不符合的留下;allow_domains则是符合匹配的域名的链接留下,这样可以避免跳到其他网站爬取不相关的页面;

栗子说明:

Rule(LinkExtractor(allow=r'page=\d+'), follow=True)

该规则表示当前页面中的列表页的链接提取和页面信息的处理,根据页面的规则来看,翻页的链接规则是page=\d+,如图1;follow=True则表示在新页面中若发现有链接符合page=\d+,也将该链接添加到爬取的链接中;如下,翻页的链接中都是在链接后添加参数page=数字,精彩问答总共有100页,如果follow=False的话,那爬虫就只爬取了入口页一页的问答列表,没有爬取2~100页的问答列表页,follow=True的话就会在入口页page=1的时候,获取page=2,3,4,5,6,7,8的翻页链接,在page=100的时候,获取page=93,94,95,96,97,98,99的列表页链接了。


图1 翻页

Rule(LinkExtractor(allow='question'), callback="parse_item", follow=False)

该规则则是爬取了问答详情页,将详情页的数据发给parse_item()函数进行处理
(2)parse_item(self, response)函数对爬取到的详情页面进行数据的解析处理

    def parse_item(self, response):
        item = GuokrItem()
        # 获取url
        item['url'] = response.url
        # 获取问题
        item['question'] = response.xpath('//h1[@id="articleTitle"]/text()').extract_first()
        # 获取问题的多个回答
        item['answers'] = []
        answers_data = response.css('.answer-txt')
        for answer_data in answers_data:
            texts = answer_data.css('p::text').extract()
            answer = ''
            for txt in texts:
                if txt != ' ':
                    answer = answer + txt
            item['answers'].append(answer)
        yield item

问题question字段:


问题

回答answers字段:


答案

数据解析完成之后,返回数据给engine,由engine转发给管道pipeline对数据进行存储操作

5、数据存储

另外编写了mongoSave.mongoSave类将数据保存到mongo数据库
mongoSave.py

import pymongo
from scrapy.conf import settings

class mongoSave(object):
    def __init__(self):
        mongos = settings['MONGOS']
        connection = pymongo.MongoClient(host=mongos['host'], port=mongos['port'])
        database = connection[mongos['db']]
        self.collection = database[mongos['collection']]

    def process_item(self, item, spider):
        if item['question'] != None:
            self.collection.insert(dict(item))
        return item

需要在settings配置文件中添加mongo配置

MONGOS = {
   "host": "127.0.0.1",
   "port": 27017,
   "db": "study",
   "collection": "guokr"
}

6、关于中间件middlewares.py的一些内容

(1)UserAgentMiddleware(object):每个请求中添加随机的user-agent

class UserAgentMiddleware(object):
    def __init__(self):
        self.user_agent_list = [
            'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.87 Safari/537.36',
            "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; AcooBrowser; .NET CLR 1.1.4322; .NET CLR 2.0.50727)",
            "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0; Acoo Browser; SLCC1; .NET CLR 2.0.50727; Media Center PC 5.0; .NET CLR 3.0.04506)",
            "Mozilla/4.0 (compatible; MSIE 7.0; AOL 9.5; AOLBuild 4337.35; Windows NT 5.1; .NET CLR 1.1.4322; .NET CLR 2.0.50727)",
            "Mozilla/5.0 (Windows; U; MSIE 9.0; Windows NT 9.0; en-US)",
            "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Win64; x64; Trident/5.0; .NET CLR 3.5.30729; .NET CLR 3.0.30729; .NET CLR 2.0.50727; Media Center PC 6.0)",
            "Mozilla/5.0 (compatible; MSIE 8.0; Windows NT 6.0; Trident/4.0; WOW64; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; .NET CLR 1.0.3705; .NET CLR 1.1.4322)",
            "Mozilla/4.0 (compatible; MSIE 7.0b; Windows NT 5.2; .NET CLR 1.1.4322; .NET CLR 2.0.50727; InfoPath.2; .NET CLR 3.0.04506.30)",
            "Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN) AppleWebKit/523.15 (KHTML, like Gecko, Safari/419.3) Arora/0.3 (Change: 287 c9dfb30)",
            "Mozilla/5.0 (X11; U; Linux; en-US) AppleWebKit/527+ (KHTML, like Gecko, Safari/419.3) Arora/0.6",
            "Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1.2pre) Gecko/20070215 K-Ninja/2.1.1",
            "Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN; rv:1.9) Gecko/20080705 Firefox/3.0 Kapiko/3.0",
            "Mozilla/5.0 (X11; Linux i686; U;) Gecko/20070322 Kazehakase/0.4.5",
            "Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.8) Gecko Fedora/1.9.0.8-1.fc10 Kazehakase/0.5.6",
            "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/535.11 (KHTML, like Gecko) Chrome/17.0.963.56 Safari/535.11",
            "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_3) AppleWebKit/535.20 (KHTML, like Gecko) Chrome/19.0.1036.7 Safari/535.20",
            "Opera/9.80 (Macintosh; Intel Mac OS X 10.6.8; U; fr) Presto/2.9.168 Version/11.52"
            ]

    def process_request(self, request, spider):
        # 随机选择一个user-agent,通过.headers["User-Agent"]添加进请求中
        user_agent = random.choice(self.user_agent_list)
        request.headers["User-Agent"] = user_agent

    def process_response(self, request, response, spider):
        # 查看请求头的User-Agent是否添加成功
        user_agent = request.headers['User-Agent']
        print(user_agent)
        return response

user-agent的处理是在下载中间件中设置的,所以要在settings配置文件中的DOWNLOADER_MIDDLEWARES字段中添加该中间件:'GuoKr.middlewares.UserAgentMiddleware': 444
注:数字444只是代表中间件的权重

(2)ProxyMiddleware(object):使用代理进行请求

class ProxyMiddleware(object):
    def process_request(self, request, spider):
        proxy = "118.187.58.34:53281"
        # 通过.meta['proxy']添加代理设置
        request.meta['proxy'] = proxy

代理设置同样是在下载中间件中设置的,所以要在settings配置文件中的DOWNLOADER_MIDDLEWARES字段中添加该中间件:'GuoKr.middlewares.ProxyMiddleware': 333,

7、运行结果

image.png

8、遇到的问题

问题1:运行scrapy报错:ImportError: No module named win32api
原因:python找不到win32api模块
参考链接:
ImportError: no module named win32api
Python运行scrapy报错:ImportError: No module named win32api

问题2:spider.py文件中的 Rule(LinkExtractor(allow=r'page=\d+'), follow=True) 中,就算follow=True,也没有数据显示,通过print发现没有调用到parse_item()函数
原因:spider.py文件中,GuokrSpider类中的allowed_domains = ['guokr.com']误写成了'guokr.cn',导致爬取的url被过滤掉了
使用scrapy框架出现callback指定的函数不被调用的情况

问题3:问答的详情页数据有爬取到,但是engine没有调用到mongoSave中的process_item()函数进行数据的保存。
原因:一开始在spider.py的parse_item()函数中,使用了return item语句直接返回了item数据,但是engine需要的是生成器的数据,所以要使用yield item才可以
参考链接:
彻底理解Python中的yield
Scrapy----Item Pipeline的一个小问题

9、其他参考链接

Scrapy入门教程
scrapy笔记(3)将数据保存到mongo
小白进阶之Scrapy

完整代码

你可能感兴趣的:(scrapy 爬取果壳问答)