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

完整代码

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

推荐阅读更多精彩内容

  • 关于家(一) 早晨依旧在朦胧的味道中醒来... 褶皱的被子缠绕着 遮掩 阳光透过窗帘轻轻的抚着 是我们两人的温度 ...
    Mr_Deep阅读 290评论 0 1
  • 第一百四十三章 有人的地方就是麻烦 摩天轮上的美好时光也就是两三分钟而已,三人迅速的上车,便又迅速的下车了。为了让...
    chief风阅读 237评论 0 1
  • 我无聊地刷着朋友圈,又是一个寂静的夜。乌鲁木齐的夜没有大城市那般霓虹璀璨,漫漫黑夜之中零星散落着几点灯光,有些温暖...
    须渡阅读 326评论 0 4
  • 本文由艺萃原创:yicuichina 论艺术圈最骚的操作是什么?走进一家专门生产卫浴设施的商店,随手选一个男用小便...
    艺萃阅读 1,001评论 1 4