Scrapy简介
Scrapy是一个为了爬取网站数据,提取结构性数据而编写的应用框架。 可以应用在包括数据挖掘,信息处理或存储历史数据等一系列的程序中。
Scrapy入门
主要完成以下几个步骤:
- 创建一个Scrapy项目
- 定义提取的Item
- 编写爬取网站的 spider 并提取 Item
- 编写 Item Pipeline 来存储提取到的Item(即数据)
创建项目:
在开始爬取之前,必须创建一个新的Scrapy项目。 进入打算存储代码的目录中:
运行下列命令:
scrapy startproject scrapy_demo
该命令将会创建包含下列内容的 scrapy_demo 目录:
scrapy_demo/
scrapy.cfg
scrapy_demo/
__init__.py
items.py
pipelines.py
settings.py
spiders/
__init__.py
...
这些文件分别是:
- scrapy.cfg: 项目的配置文件
- scrapy_demo/: 该项目的python模块。之后将在此加入代码
- scrapy_demo/items.py: 项目中的item文件
- scrapy_demo/pipelines.py: 项目中的pipelines文件
- scrapy_demo/settings.py: 项目的设置文件
- scrapy_demo/spiders/: 放置spider代码的目录
定义Item:
Item 是保存爬取到的数据的容器;其使用方法和python字典类似, 并且提供了额外保护机制来避免拼写错误导致的未定义字段错误。
- 在这里尝试爬取新浪新闻滚动版,点开任意一条新闻,查看网页源代码,从中观察到可以提取的信息内容,这里列出其中两种:
- title
- description
- 通过创建一个 scrapy.Item 类, 并且定义类型为 scrapy.Field 的类属性来定义一个Item。 首先根据需要从刚才的网站上获取到的数据对item进行建模。 需要从中获取以上几种信息。 对此,在item中定义相应的字段。编辑 scrapy_demo 目录中的 items.py 文件:
import scrapy
class ScrapyDemoItem(scrapy.Item):
# define the fields for your item here like:
# name = scrapy.Field()
title = scrapy.Field()
tags = scrapy.Field()
keywords = scrapy.Field()
description = scrapy.Field()
通过定义item, 可以很方便的使用Scrapy的其他方法。而这些方法需要知道item的定义。
编写爬虫(Spider)
Spider是用户编写用于从单个网站(或者一些网站)爬取数据的类。其包含了一个用于下载的初始URL,如何跟进网页中的链接以及如何分析页面中的内容, 提取生成 item 的方法。为了创建一个Spider,必须继承 scrapy.Spider 类, 且定义以下三个属性:
- name: 用于区别Spider。 该名字必须是唯一的,不可以为不同的Spider设定相同的名字。
- start_urls: 包含了Spider在启动时进行爬取的url列表。 因此,第一个被获取到的页面将是其中之一。 后续的URL则从初始的URL获取到的数据中提取。
- parse() 是spider的一个方法。 被调用时,每个初始URL完成下载后生成的 Response 对象将会作为唯一的参数传递给该函数。 该方法负责解析返回的数据(response data),提取数据(生成item)以及生成需要进一步处理的URL的 Request 对象。
以下为Spider代码,保存在 scrapy_demo/spiders 目录下的 demo_spider.py 文件中:
import scrapy
class DemoSpider(scrapy.spiders.Spider):
name = "demo"
start_urls = [
"https://mil.news.sina.com.cn/jssd/2020-04-14/doc-iircuyvh7664688.shtml"
]
def parse(self, response):
# filename = response.url.split("/")[-2]
filename = 'news'
with open(filename, 'wb') as f:
f.write(response.body)
-
爬取
进入项目的根目录下,并执行命令启动spider:
执行后得到类似的输出:
可以在文件夹中发现创建了一个名为news的输出,该输出可以用vscode打开查看:
解释:Scrapy为Spider的 start_urls 属性中的每个URL创建了 scrapy.Request 对象,并将 parse 方法作为回调函数(callback)赋值给了Request。Request对象经过调度,执行生成 scrapy.http.Response 对象并送回给spider parse() 方法。 -
提取Item
从news中可以看出,网页的相关信息都保存在news中:
现在就要从其中提取出我们需要的信息,这里用到了一种基于XPath和CSS表达机制的工具: Scrapy Selectors。这里表示XPath表达式的示例及对应的含义:- /html/head/title:选择HTML文档中标签内的
元素 - /html/head/title/text():选择上面提到的
元素的文字 - //td:选择所有的元素
- //div[@class=“mine”]:选择所有具有class="mine"属性的div元素
Selector有四个基本的方法(单击相应的方法可以看到详细的API文档):
- xpath():预定的xpath表达式,返回该表达式所对应的所有异步的选择器列表列表。
- css():初始CSS表达式,返回该表达式所对应的所有队列的选择器列表。
- extract():序列化该例程为unicode字符串并返回列表。
- re():根据初步的正则表达式对数据进行提取,返回unicode字符串列表列表。
具体尝试如下,基本可以一目了然:
-
首先打开Scrapy shell,在shell中观察网页信息:在项目根目录下执行以下命令:会显示如下:
-
在其中输入response.body会得到response对象中的body信息。
-
结合用vscode打开的news文件,做以下尝试:
可以看出距离我们像提取的文章信息已经很近了,接下来要完成的就是去掉一些符号、换行等import re class DemoSpider(scrapy.spiders.Spider): name = "demo" start_urls = [ "https://mil.news.sina.com.cn/jssd/2020-04-14/doc-iircuyvh7664688.shtml" ] def parse(self, response): title = response.xpath('//title/text()').extract() title = ''.join(title) content = response.xpath('//div[@class="article" and @id="article"]//p/text() ').extract() # 将list里的所有内容串起来变成字符串 content = ''.join(content) content = re.sub(r'\u3000', '', content) content = content.replace( "▲", '') print("title:{}".format(title)) print("content:{}".format(content))
-
在项目的根目录的命令行输入:scrapy crawl demo,运行结果如下:
可以看出信息被成功输出
- /html/head/title:选择HTML文档中标签内的
-
使用item
Item 对象是自定义的python字典。 一般来说,Spider将会将爬取到的数据以 Item 对象返回。所以为了将爬取的数据返回,最终的代码将是:```python import scrapy import re from scrapy_demo.items import ScrapyDemoItem class DemoSpider(scrapy.spiders.Spider): name = "demo" start_urls = [ "https://mil.news.sina.com.cn/jssd/2020-04-14/doc-iircuyvh7664688.shtml" ] def parse(self, response): item = ScrapyDemoItem() title = response.xpath('//title/text()').extract() title = ''.join(title) content = response.xpath('//div[@class="article" and @id="article"]//p/text() ').extract() # 将list里的所有内容串起来变成字符串 content = ''.join(content) content = re.sub(r'\u3000', '', content) content = content.replace( "▲", '') # print("title:{}".format(title)) # print("content:{}".format(content)) item['title'] = title item['content'] = content yield item ```
-
将爬取到的数据保存为本地json
项目的根目录下,在命令行输入:
即可将爬取数据保存为json且编码为“UTF-8”