scrapy分页抓取网页(Scrapy轻松定制网络爬虫2009-08-14网络)
优采云 发布时间: 2021-12-31 05:17scrapy分页抓取网页(Scrapy轻松定制网络爬虫2009-08-14网络)
Scrapy 轻松定制网络爬虫 2009-08-14 网络爬虫(Spider)是一种在互联网上爬行的机器人。当然,它通常不是实体机器人,因为网络本身也是一个虚拟的东西,所以这个“机器人”其实就是一个程序,不是爬行,而是有一定的用途,爬行的时候会采集. 一些信息。例如,谷歌有大量的爬虫采集网页内容和它们之间的链接信息;再比如,一些别有用心的爬虫在网上采集诸如foo bar[dot] com之类的东西。此外,还有一些定制的爬虫,专门针对某个网站。例如,不久前,JavaEye写了几篇专门对付恶意爬虫的博客(原链接好像已经过期了,就不给了),还有网站比如小众软件或者LinuxToy经常被全站爬下来并以不同的名字出去玩。其实,爬虫的基本原理非常简单。只要能上网,能分析网页,现在大多数语言都有方便的Http客户端库可以抓取网页,最简单的HTML分析可以直接使用正则规则。表达式做到了,所以做最简单的网络爬虫其实是一件很简单的事情。爬虫的基本原理非常简单。只要能上网,能分析网页,现在大多数语言都有方便的Http客户端库可以抓取网页,最简单的HTML分析可以直接使用正则规则。表达式做到了,所以做最简单的网络爬虫其实是一件很简单的事情。爬虫的基本原理非常简单。只要能上网,能分析网页,现在大多数语言都有方便的Http客户端库可以抓取网页,最简单的HTML分析可以直接使用正则规则。表达式做到了,所以做最简单的网络爬虫其实是一件很简单的事情。
但是,要实现一个高质量的蜘蛛是非常困难的。爬虫的两部分是下载网页。需要考虑的问题有很多,比如如何最大限度地利用本地带宽,如何调度不同站点的web请求,以减少其他服务器的负担。在高性能的Web Crawler系统中,DNS查询也将成为亟待优化的瓶颈。此外,还有一些“配置文件”需要遵循(例如,robots.txt)。获取网页后的分析过程也非常复杂。网上奇奇怪怪的东西很多,各种HTML页面出现各种错误。要清楚地分析所有这些几乎是不可能的。另外,随着AJAX的流行,如何获取Javascript动态生成的内容已经成为一个大问题;此外,网络上还有各种有意无意出现的蜘蛛陷阱。如果一味的跟着超链接走,就会被困在陷阱里。比如这个网站,据说谷歌之前声称互联网UniqueURL的数量已经达到万亿,所以这个人是第二万亿。然而,实际上并没有多少人需要像谷歌那样制作一个通用的爬虫。通常我们做一个爬虫来爬取对于特定的一种或某种类型的网站,所谓知己知彼,百战不殆,我们可以对网站@进行一些分析> 需要提前爬取的结构,事情会变得容易很多。互联网上有各种有意或无意出现的蜘蛛陷阱。如果一味的跟着超链接走,就会被困在陷阱里。比如这个网站,据说谷歌之前声称互联网UniqueURL的数量已经达到万亿,所以这个人是第二万亿。然而,实际上并没有多少人需要像谷歌那样制作一个通用的爬虫。通常我们做一个爬虫来爬取对于特定的一种或某种类型的网站,所谓知己知彼,百战不殆,我们可以对网站@进行一些分析> 需要提前爬取的结构,事情会变得容易很多。互联网上有各种有意或无意出现的蜘蛛陷阱。如果一味的跟着超链接走,就会被困在陷阱里。比如这个网站,据说谷歌之前声称互联网UniqueURL的数量已经达到万亿,所以这个人是第二万亿。然而,实际上并没有多少人需要像谷歌那样制作一个通用的爬虫。通常我们做一个爬虫来爬取对于特定的一种或某种类型的网站,所谓知己知彼,百战不殆,我们可以对网站@进行一些分析> 需要提前爬取的结构,事情会变得容易很多。比如这个网站,据说谷歌之前声称互联网UniqueURL的数量已经达到万亿,所以这个人是第二万亿。然而,实际上并没有多少人需要像谷歌那样制作一个通用的爬虫。通常我们做一个爬虫来爬取对于特定的一种或某种类型的网站,所谓知己知彼,百战不殆,我们可以对网站@进行一些分析> 需要提前爬取的结构,事情会变得容易很多。比如这个网站,据说谷歌之前声称互联网UniqueURL的数量已经达到万亿,所以这个人是第二万亿。然而,实际上并没有多少人需要像谷歌那样制作一个通用的爬虫。通常我们做一个爬虫来爬取对于特定的一种或某种类型的网站,所谓知己知彼,百战不殆,我们可以对网站@进行一些分析> 需要提前爬取的结构,事情会变得容易很多。
通过分析和选择有价值的链接进行跟踪,我们可以避免很多不必要的链接或蜘蛛陷阱。如果网站的结构允许选择合适的路径,我们就可以按照感兴趣的事物的一定顺序再次爬取它,这样就连URL重复的判断都可以省略了。比如我们要爬下pongba中的博客文字,通过观察,很容易发现我们感兴趣的页面有两个:文章列表页面,比如首页,或者URL / page/\d+ /对于这样的页面,通过Firebug可以看到每篇文章的链接都在一个h1标签中(注意Firebug HTML面板中看到的HTML代码和查看源代码可能会有些不同另外,如果网页中有Javascript动态修改DOM树,前者是修改后的版本,由 Firebug 规范化。比如属性有引号等,而后者通常是蜘蛛爬取的原创内容。如果是使用正则表达式来分析页面或者使用HTML Parser Firefox 如果有一些出入,则需要特别注意),此外,在wp-pagenavi div 类中还有指向不同列表页面的链接。
文章内容页,每个博客都有这样一个页面,例如/2008/09/11/machine-learning-and-ai-resources/,收录了文章的完整内容,这是我们的感受感兴趣的内容。因此,我们从首页开始,利用 wp-pagenavi 中的链接来获取其他 文章 列表页面。特别地,我们定义了一个路径:followNext Page 链接,这样我们就可以从头到尾依次遍历。免去判断重复爬行的麻烦。另外,文章列表页上链接到具体文章的链接对应的页面就是我们真正要保存的数据页。这样一来,其实用的脚本语言写一个ad hoc Crawler来完成这个任务并不难,不过今天的主角是Scrapy。这是一个Python CrawlerFramework,简单,轻量,非常方便,并且官网说已经在实际生产中。在使用中,它不是玩具级别的东西。不过目前还没有Release版本,可以直接使用他们的Mercurial库来获取源码进行安装。不过这个东西也可以不安装直接使用,方便随时更新。文档很详细,不再赘述。Scrapy 使用异步网络库 Twisted 来处理网络通信。架构清晰,收录各种中间件接口,可以灵活满足各种需求。这个东西也可以不用安装直接使用,方便随时更新。文档很详细,不再赘述。Scrapy 使用异步网络库 Twisted 来处理网络通信。架构清晰,收录各种中间件接口,可以灵活满足各种需求。这个东西也可以不用安装直接使用,方便随时更新。文档很详细,不再赘述。Scrapy 使用异步网络库 Twisted 来处理网络通信。架构清晰,收录各种中间件接口,可以灵活满足各种需求。
整体架构如下图所示: 绿线为数据流。首先从初始的URL开始,Scheduler将其交给Downloader进行下载,下载完成后交给Spider进行分析。Spider分析的结果有两种:一种是需要进一步爬取的Links,比如之前分析到的“下一页”的链接,会返回给Scheduler;另一个是需要保存的数据,它们会被送到Item Pipeline,也就是执行数据Place进行后处理(详细分析、过滤、存储等)。此外,数据流通道中可以安装各种中间件。执行必要的部分似乎很复杂。使用起来非常简单。和 Rails 一样,首先新建一个项目:scrapy-admin。py startproject blog_crawl 会创建一个blog_crawl目录,里面有一个scrapy-ctl.py是整个项目的控制脚本,代码全部放在子目录blog_crawl下。为了能够爬行,我们在spiders目录下新建一个mindhacks_spider.py,定义我们的Spider如下: from scrapy.spider import BaseSpider class MindhacksSpider(BaseSpider): domain_name ""start_urls [""]def parse(self , response): return MindhacksSpider() 我们的MindhacksSpider继承自BaseSpider(通常直接继承自scrapy.contrib.spiders,功能更多。CrawlSpider更方便,但为了展示数据是如何解析的,这里使用了BaseSpider),变量 domain_name start_urls 的意思很容易理解,而parse方法就是我们需要定义的回调函数。默认请求会在响应之后调用这个回调函数,我们需要解析这里的页面并返回两个结果(需要进一步爬取的链接和需要保存的数据)。让我觉得有点奇怪的是,它的接口定义中的两个结果实际上是混合在一个列表中返回的,不清楚为什么会出现这种设计,你到底要不要努力把它们分开? 总之,这里我们先写一个空函数,它只返回一个空列表。让我觉得有点奇怪的是,它的接口定义中的两个结果实际上是混合在一个列表中返回的,不清楚为什么会出现这种设计,你到底要不要努力把它们分开? 总之,这里我们先写一个空函数,它只返回一个空列表。让我觉得有点奇怪的是,它的接口定义中的两个结果实际上是混合在一个列表中返回的,不清楚为什么会出现这种设计,你到底要不要努力把它们分开? 总之,这里我们先写一个空函数,它只返回一个空列表。
另外,定义一个“全局”变量SPIDER,当Scrapy进入这个模块时会被实例化,并且会被Scrapy引擎自动找到。所以可以先运行爬虫试试: ./scrapy-ctl.py crawl 会有一堆输出,可以看到爬取了,因为这是初始的URL,但是因为我们在解析号里没有返回,我们需要进一步爬取URL,所以整个爬取过程只爬到了首页就结束了。下一步是分析页面。Scrapy 提供了一个非常方便的 Shell(需要 IPython),可以让我们进行实验。使用以下命令启动Shell ./scrapy-ctl.pyshell 它将启动爬虫并指定命令行抓取此页面并进入shell。根据提示,我们有很多现成的变量可以使用。其中之一是 hxs,这是一个 HtmlXPathSelector。mindhacks HTML 页面相对标准,可以很容易地直接使用 XPath 进行分析。通过Firebug可以看到每个博客文章的链接都在h1下,所以在Shell中使用这个XPath表达式测试:hxs.x(´//h1/a/@href´ ).extract()Out [1]: [u´´, u´´, u´´, u´´, u´´, u´´, u´´, u´´, u´´, u´´ ] 这正是URL我们需要。此外,您还可以找到“下一页”的链接,该链接与其他几个页面的链接在一个div 中。但是“下一页”的链接没有title属性,所以XPath写//div[@class="wp-pagenavi" 所以在 Shell 中使用这个 XPath 表达式测试: hxs.x(´//h1/a/@href´ ).extract()Out[1]: [u´´, u´´, u´´, u´´, u´´, u´´, u´´, u´´, u´´, u´´ ] 这正是我们需要的 URL。此外,您还可以找到“下一页”的链接,该链接与其他几个页面的链接在一个div 中。但是“下一页”的链接没有title属性,所以XPath写//div[@class="wp-pagenavi" 所以在 Shell 中使用这个 XPath 表达式测试: hxs.x(´//h1/a/@href´ ).extract()Out[1]: [u´´, u´´, u´´, u´´, u´´, u´´, u´´, u´´, u´´, u´´ ] 这正是我们需要的 URL。此外,您还可以找到“下一页”的链接,该链接与其他几个页面的链接在一个div 中。但是“下一页”的链接没有title属性,所以XPath写//div[@class="wp-pagenavi"
需要注意的是,这里返回的列表并非都是字符串格式的URL。Scrapy希望得到Request对象,它可以携带比字符串格式的URL更多的东西,比如cookies或者回调函数。某种类型的。可以看到我们在创建博客正文的请求时替换了回调函数,因为默认的回调函数parse是专门用来解析文章列表等页面的。parse_post 定义如下: def parse_post(self, response): item BlogCrawlItem()item.url unicode(response.url)item.raw response.body_as_unicode()return [item] 很简单,返回一个BlogCrawlItem,把抓到的里面的数据,这里可以稍微分析一下,比如文本和标题是通过XPath解析的,但是我倾向于后期做这些事情,比如Item Pipeline或者后期Offline阶段。BlogCrawlItem Scrapy 自动帮我们定义了一个很好的继承自ScrapedItem items.py,这里我加了一句: from scrapy.item import ScrapedItem class BlogCrawlItem(ScrapedItem): def __init__(self): ScrapedItem.__init__(self) self. url def__str__(self): return ´BlogCrawlItem(url: self.url 定义了__str__函数,只给出了url,因为默认的__str__函数会显示所有的数据,所以在爬取Log的时候会看到控制台疯狂输出东西,即输出抓取到的网页内容。
-.-bb 这样就检索到了数据。最后只剩下存储数据的功能了。我们通过添加流水线来实现它。由于Python标准库自带Sqlite3支持,所以我使用Sqlite数据库来存储数据。. 将pipelines.py的内容替换为以下代码:1011 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 import sqlite3 from os core 从scrapy导入。 xlib. pydispatch 导入调度器类 SQLiteStorePipeline(object): 文件名 ´data.sqlite´def __init__(self): self.conn Nonedispatcher.connect(self.initialize,signals.engine_started) dispatcher.connect(self.finalize,signals.engine_stopped) def process_item (self, domain, item): self.conn.execute(´insert blogvalues(?,?,?)´, (item.url, item.raw, unicode(domain))) return item def initialize(self):
当数据通过管道时,将调用 process_item 函数。这里我们将原创数据直接存入数据库,不做任何处理。如有必要,您可以添加额外的管道来提取和过滤数据。我不会在这里详述。最后,在 settings.py [´blog_crawl.pipelines.SQLiteStorePipeline´] 中列出我们的管道并运行爬虫。最后总结一下:一个高质量的爬虫是一个极其复杂的工程,但是如果你有一个好的工具,做一个专用的爬虫是相对容易的。Scrapy是一个非常轻量级的爬虫框架,极大的简化了爬虫的开发过程。另外,Scrapy的文档也很详细。如果觉得我的介绍省略了一些不清楚的地方,推荐阅读他的Tutorial