预备知识: Scrapy框架主要组成部分:
Scrapy Engine
:Scrapy
引擎,负责控制整个系统的数据流和事件的触发;
Scheduler
:调度器,接收 Scrapy 引擎发来的请求并将其加入队列中,等待引擎后续需要时使用;
Downloader
:下载器,爬取网页内容,将爬取到的数据返回给Spiders
(爬虫);
Spiders
:爬虫,这部分是核心代码,用于解析、提取出需要的数据;
Item Pipeline
: 数据管道,处理提取出的数据,主要是数据清洗、验证和数据存储;
Downloader middlewares
:下载器中间件,处理 Scrapy
引擎和下载器之间的请求和响应;
Spider middlewares
:爬虫中间件,处理爬虫输入的响应以及输出结果或新的请求。
1 2 3 4 5 6 7 8 9 10 Scrapy 中数据流的过程如下: Scrapy 引擎打开一个网站,找到处理该网站对应的爬虫,并爬取网页的第一个页面; Scrapy 引擎从爬虫中获取第一个页面地址,并将其作为请求放进调度器中进行调度; Scrapy 引擎从调度器中获取下一个页面的地址; 调度器返回下一个页面的地址给 Scrapy 引擎,Scrapy 引擎通过下载器中间件传递给下载器进行爬取; 爬取到数据后,下载器通过下载器中间件回传给 Scrapy 引擎; Scrapy 引擎将爬取到的数据通过爬虫中间件传递给爬虫进行数据解析、提取; 爬虫处理完数据后,将提取出的数据和新的请求回传给 Scrapy 引擎; Scrapy 将提取出的数据传给数据管道进行数据清洗等操作,同时将新的请求传递给调度器准备进行下一页的爬取; 重复 2 -8 步,直到调度器中没有新的请求,数据爬取结束。
scrapy框架命令:
1 2 3 4 scrapy startproject [工程名称] cd [工程名称]scrapy genspider [爬虫名称] [网站地址]
配置spider: scrapy框架中的Request类:
Response类:
输入scrapy
输入explorer .
在所打开的文件夹内即可创建文件进行编码(记得将txt文本改成py后缀)
然后再指令中输入:
1 2 3 4 scrapy runspider spider.py -t csv -o apps.csv
实战: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 import scrapyclass TitleSpider (scrapy.Spider ): name = 'title-spider' start_urls = ['https://www.appinn.com/category/windows/' ] def parse (self, response ): for article in response.css('article' ): a = article.css('h2.title a' ) if a: result = { 'title' : a.attrib['title' ], 'url' : a.attrib['href' ], } yield result next_page = response.css('a.next::attr(href)' ).get() if next_page is not None : yield response.follow(next_page, self.parse)
CSS选择器 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 css选择器 按标签名选择: response.css('标签名' ) 例如写 response.css('ul' ) 就可以选择出所有 ul 元素,response.css('div' ) 可以选择出 div 元素。 按class 选择:response .css ('.class名' ) 当我们想要选择 class 包含 container 的 div 元素,我们可以写 response .css ('div.container' )。 按id 选择: response .css ('#id名' ) response .css ('a#pull' )表示我们想选择一个 id 为 pull 的 a 元素按层级关系选择:用一个空格隔开表示后面为前面所属 空格后面表示的是所有的子元素,不管是处于多少层的子元素。 而如果只想要第一层的子元素则应该用 > 分隔开。这里的 a 元素就是第一层的子元素 所以 h2 .title .post -title a 和 h2 .title .post -title > a 这两种写法的效果是一样的 取元素中的文本: 使用: :text例如response.css('a::text' )表示得到所有a标签下的文本,但得到的并不是纯文本形式 如果需要得到纯文本: response.css('h2.title.post-title a::text' ).get() response.css('h2.title.post-title a::text' ).getall() 取元素的属性:需要调用attrib方法 如果我们想得到这个 a 元素的 href 属性,需要调用这个元素的 attrib 属性 a = response.css('h2.title.post-title a' ) a.attrib['href' ] 所有符合 h2.title.post-title a 这个条件的标签的 href 属性,就像下面这样: for a in response.css('h2.title.post-title a' ): print (a.attrib['href' ]) 或者另一种写法也可以取到 href 属性,就是加上 ::attr(href): for href in response.css('h2.title.post-title a::attr(href)' ).getall(): print (href)
Scrapy互动工具:
scrapy shell “https://www.appinn.com/category/windows/ “
下面是Scrapy的整个项目配置流程:
启动一个Scrapy项目
首先输入 explorer . 进入指定文件夹
然后输入 scrapy startproject appinn 让Scarpy创建一个名字叫做appinn的项目
然后输入 cd appinn进入指定文件夹
1 2 3 4 5 6 7 8 9 10 11 items.py items.py记录了我们想要的数据格式的文件,打开items.py import scrapyclass AppinnItem (scrapy.Item ): name = scrapy.Field link = scrapy.Field pass
spiders 目录里保存了我们创建的爬虫。
在命令行里执行下面的命令并回车,它会帮我们创建一个叫做 article 的爬虫,
并且只爬取 www.appinn.com 下的网页:
scrapy genspider article www.appinn.com
在spider中打开创建的article.py:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 import scrapyfrom appinn.items import Articleclass ArticleSpider (scrapy.Spider ): name = 'article' allowed_domains = ['www.appinn.com' ] start_urls = ['https://www.appinn.com/category/windows/' ] def parse_article (self, response ): article = Article() article['title' ] = response.css('' ).get() article['time' ] = response.css('' ).get() article['author' ] = response.css('' ).get() article['score' ] = response.css('' ).get() contents = response.css('' ).getall() article['content' ] = '\n' .join(contents) yield article def parse (self, response ): for article_url in response.css('' ).getall(): if not article_url: continue yield response.follow(article_url, self.parse_article) next_page = response.css('' ).get() if next_page: yield response.follow(next_page, self.parse) pass
name 是项目中每个爬虫唯一的名字,用来区分不同的爬虫。
allowed_domains 是允许爬取的域名,如果请求的链接不在这个域名下,那么这些请求将会被过滤掉。
start_urls 是初始请求地址的列表,也就是一开始就爬取的页面地址列表。
parse() 方法是默认的解析方法,负责解析返回的响应、提取数据或者生成下一步要处理的请求
首先我们把 start_urls 改成我们需要的开始链接
接着引入我们刚刚写好的 Item
下面是加一个解析文章的方法,用来解析文章详情页,把标题、时间、作者、分数、正文取出来
1 2 3 4 5 6 7 8 9 10 11 12 13 def parse_article (self, response ): article = Article() article['title' ] = response.css('h1.title::text' ).get() article['time' ] = response.css('span.thetime span::text' ).get() article['author' ] = response.css('span.theauthor span a::text' ).get() article['score' ] = response.css('em strong::text' ).get() contents = response.css('div.post-single-content *::text' ).getall() article['content' ] = '\n' .join(contents) yield article response.follow() 方法用于生成下一个请求和数据解析
完整版的 article.py 如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 import scrapyfrom appinn.items import Articleclass ArticleSpider (scrapy.Spider ): name = 'article' allowed_domains = ['www.appinn.com' ] start_urls = ['https://www.appinn.com/category/windows/' ] def parse (self, response ): for article_url in response.css('article h2.title a::attr(href)' ).getall(): if not article_url: continue yield response.follow(article_url, self.parse_article) next_page = response.css('a.next::attr(href)' ).get() if next_page: yield response.follow(next_page, self.parse) def parse_article (self, response ): article = Article() article['title' ] = response.css('h1.title::text' ).get() article['time' ] = response.css('span.thetime span::text' ).get() article['author' ] = response.css('span.theauthor span a::text' ).get() article['score' ] = response.css('em strong::text' ).get() contents = response.css('div.post-single-content *::text' ).getall() article['content' ] = '\n' .join(contents) yield article
pipelines.py
pipelines.py 文件允许我们在得到结果之后,对结果进行一些处理。即筛选数据
初始化:
1 2 3 4 5 6 7 8 9 10 class AppinnPipeline (object ): def process_item (self, item, spider ): if item.get('想要筛的数据名称' ): if item['score' ]: raise DropItem('' ) return item process_item() 方法就是得到结果之后处理 item 的方法 raise DropItem() 可以去掉我们不想要的 item
settings.py
settings.py 是整个项目的配置文件这个文件里可以设置爬取并发个数、等待时间、输出格式、默认 header 等等
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 BOT_NAME = 'appinn' SPIDER_MODULES = ['appinn.spiders' ] NEWSPIDER_MODULE = 'appinn.spiders' ROBOTSTXT_OBEY = True pipelineITEM_PIPELINES = { 'appinn.pipelines.AppinnPipeline' : 300 , } FEED_FORMAT = 'csv' AUTOTHROTTLE_ENABLED = TrueAUTOTHROTTLE_TARGET_CONCURRENCY = 5