Python下用Scrapy和MongoDB构建爬虫系统(2)
admin
2023-07-30 22:03:47
0

在上一篇中,我们实现了一个基本网络爬虫,它可以从StackOverflow上下载最新的问题,并将它们存储在MongoDB数据库中。在本文中,我们将对其扩展,使它能够爬取每个网页底部的分页链接,并从每一页中下载问题(包含问题标题和URL)。

在你开始任何爬取工作之前,检查目标网站的使用条款并遵守robots.txt文件。同时,做爬取练习时遵守道德,不要在短时间内向某个网站发起大量请求。像对待自己的网站一样对待任何你将爬取的网站。

开始

有两种可能的方法来接着从上次我们停下的地方继续进行。

第一个方法是,扩展我们现有的网络爬虫,通过利用一个xpath表达式从”parse_item”方法里的响应中提取每个下一页链接,并通过回调同一个parse_item方法产生一个请求对象。利用这种方法,爬虫会自动生成针对我们指定的链接的新请求,你可以在Scrapy文档这里找到更多有关该方法的信息。

另一个更简单的方法是,使用一个不同类型的爬虫—CrawlSpider(链接)。这是基本Spider的一个扩展版本,它刚好满足我们的要求。

CrawlSpider

我们将使用与上一篇教程中相同的爬虫项目,所以如果你需要的话可以从repo上获取这些代码。

创建样板

在“stack”目录中,首先由crawl模板生成爬虫样板。

123 $ scrapy genspider stack_crawler  stackoverflow.com t crawlCreated spider 'stack_crawler' using template 'crawl' in module:  stack.spiders.stack_crawler

Scrapy项目现在看起来应该像这样:

12345678910 ├── scrapy.cfg└── stack    ├── __init__.py    ├── items.py    ├── pipelines.py    ├── settings.py    └── spiders        ├── __init__.py        ├── stack_crawler.py        └── stack_spider.py

stack_crawler.py文件内容如下:

12345678910111213141516171819202122 # -*- coding: utf-8 -*-import scrapyfrom scrapy.contrib.linkextractors import LinkExtractorfrom scrapy.contrib.spiders import CrawlSpider, Rule from stack.items import StackItem class StackCrawlerSpider(CrawlSpider):    name = \’stack_crawler\’    allowed_domains = [\’stackoverflow.com\’]    start_urls = [\’http://www.stackoverflow.com/\’]     rules = (        Rule(LinkExtractor(allow=r\’Items/\’), callback=\’parse_item\’, follow=True),    )     def parse_item(self, response):        i = StackItem()        #i[\’domain_id\’] = response.xpath(\’//input[@id=\”sid\”]/@value\’).extract()        #i[\’name\’] = response.xpath(\’//div[@id=\”name\”]\’).extract()        #i[\’description\’] = response.xpath(\’//div[@id=\”description\”]\’).extract()        return i

我们只需要对这个样板做一些更新。

更新“start_urls”列表

首先,添加问题的第一个页面链接到start_urls列表:

123 start_urls = [    \’http://stackoverflow.com/questions?pagesize=50&sort=newest\’]

更新“rules”列表

接下来,我们需要添加一个正则表达式到“rules”属性中,以此告诉爬虫在哪里可以找到下一个页面链接:

1234 rules = [    Rule(LinkExtractor(allow=r\’questions?page=[0-9]&sort=newest\’),         callback=\’parse_item\’, follow=True)]

现在爬虫能根据那些链接自动请求新的页面,并将响应传递给“parse_item”方法,以此来提取问题和对应的标题。

如果你仔细查看的话,可以发现这个正则表达式限制了它只能爬取前9个网页,因为在这个demo中,我们不想爬取所有的176234个网页。

更新“parse_item”方法

现在我们只需编写如何使用xpath解析网页,这一点我们已经在上一篇教程中实现过了,所以直接复制过来。

12345678910 def parse_item(self, response):    questions = response.xpath(\’//div[@class=\”summary\”]/h3\’)     for question in questions:        item = StackItem()        item[\’url\’] = question.xpath(            \’a[@class=\”question-hyperlink\”]/@href\’).extract()[0]        item[\’title\’] = question.xpath(            \’a[@class=\”question-hyperlink\”]/text()\’).extract()[0]        yield item

这就是为爬虫提供的解析代码,但是现在先不要启动它。

添加一个下载延迟

我们需要通过在settings.py文件中设定一个下载延迟来善待StackOverflow(和任何其他网站)。

1 DOWNLOAD_DELAY = 5

这告诉爬虫需要在每两个发出的新请求之间等待5秒钟。你也很有必要做这样的限制,因为如果你不这么做的话,StackOverflow将会限制你的访问流量,如果你继续不加限制地爬取该网站,那么你的IP将会被禁止。所有,友好点—要像对待自己的网站一样对待任何你爬取的网站。

现在只剩下一件事要考虑—存储数据。

MongoDB

上次我们仅仅下载了50个问题,但是因为这次我们要爬取更多的数据,所有我们希望避免向数据库中添加重复的问题。为了实现这一点,我们可以使用一个MongoDB的 upsert方法,它意味着如果一个问题已经存在数据库中,我们将更新它的标题;否则我们将新问题插入数据库中。

修改我们前面定义的MongoDBPipeline:

123456789101112131415161718 class MongoDBPipeline(object):     def __init__(self):        connection = pymongo.Connection(            settings[\’MONGODB_SERVER\’],            settings[\’MONGODB_PORT\’]        )        db = connection[settings[\’MONGODB_DB\’]]        self.collection = db[settings[\’MONGODB_COLLECTION\’]]     def process_item(self, item, spider):        for data in item:            if not data:                raise DropItem(\”Missing data!\”)        self.collection.update({\’url\’: item[\’url\’]}, dict(item), upsert=True)        log.msg(\”Question added to MongoDB database!\”,                level=log.DEBUG, spider=spider)        return item

为简单起见,我们没有优化查询,也没有处理索引值,因为这不是一个生产环境。

测试

启动爬虫!

1 $ scrapy crawl questions

现在你可以坐下来,看着你的数据库渐渐充满数据。

结论

你可以从Github库下载整个源代码,也可以在下面评论或提问。

相关内容

热门资讯

500 行 Python 代码... 语法分析器描述了一个句子的语法结构,用来帮助其他的应用进行推理。自然语言引入了很多意外的歧义,以我们...
定时清理删除C:\Progra... C:\Program Files (x86)下面很多scoped_dir开头的文件夹 写个批处理 定...
65536是2的几次方 计算2... 65536是2的16次方:65536=2⁶ 65536是256的2次方:65536=256 6553...
Mobi、epub格式电子书如... 在wps里全局设置里有一个文件关联,打开,勾选电子书文件选项就可以了。
scoped_dir32_70... 一台虚拟机C盘总是莫名奇妙的空间用完,导致很多软件没法再运行。经过仔细检查发现是C:\Program...
pycparser 是一个用... `pycparser` 是一个用 Python 编写的 C 语言解析器。它可以用来解析 C 代码并构...
小程序支付时提示:appid和... [Q]小程序支付时提示:appid和mch_id不匹配 [A]小程序和微信支付没有进行关联,访问“小...
微信小程序使用slider实现... 众所周知哈,微信小程序里面的音频播放是没有进度条的,但最近有个项目呢,客户要求音频要有进度条控制,所...
python绘图库Matplo... 本文简单介绍了Python绘图库Matplotlib的安装,简介如下: matplotlib是pyt...
Prometheus+Graf... 一,Prometheus概述 1,什么是Prometheus?Prometheus是最初在Sound...