升级 pip 版本:
pip install --upgrade pip
通过 pip 安装 Scrapy 框架:
pip install Scrapy
在开始爬取之前,必须创建一个新的Scrapy项目。进入自定义的项目目录中,运行下列命令:
scrapy startproject mySpider
其中, mySpider 为项目名称,可以看到将会创建一个 mySpider 文件夹,目录结构大致如下:
下面来简单介绍一下各个主要文件的作用:
这些文件分别是:
Scrapy的Spider文件是爬虫程序的核心部分,它定义了如何从网站中提取数据。Spider文件通常是一个Python类,通过继承Scrapy的Spider类来定义。下面我们将详细讲解Scrapy的Spider文件。
import scrapy
class MySpider(scrapy.Spider):
name = "myspider"
allowed_domains = ["example.com"]
start_urls = ["http://www.example.com"]
def parse(self, response):
pass
在这个示例中,我们首先导入了Scrapy库。然后,定义了一个名为MySpider的Spider类,继承自Scrapy的Spider类。 接着,设置了Spider类的一些属性,包括name(爬虫程序的名称)、allowed_domains(指定爬虫程序所能访问的域名)、start_urls(指定爬虫程序的起始URL)。这些属性都是可选的,可以根据需要设置或省略。 最后,定义了一个名为parse()的方法,这是Spider程序中用于处理响应数据的核心方法。
scrapy crawl <spider_name>
其中,
创建爬虫代码的实现
在当前目录下输入命令,将在mySpider/spider目录下创建一个名为itcast的爬虫,并指定爬取域的范围:
scrapy genspider 爬虫名 "爬取域的范围,例:4399.com"
scrapy genspider game "4399.com"
定义Spider类
在Scrapy中,每个爬虫都必须是一个Spider类的实例。在Spider类中,可以定义一些属性和方法,包括爬虫名称、起始URL、回调函数等。
定义start_requests()方法
start_requests()方法用于生成初始请求,通常用于指定要爬取的起始URL和请求头部信息。如果未定义start_requests()方法,则Scrapy会默认使用默认的start_urls属性作为起始URL列表。 下面是一个start_requests()方法的示例代码:
def start_requests(self):
urls = [
'http://www.example.com/page1.html',
'http://www.example.com/page2.html',
'http://www.example.com/page3.html',
]
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3'
}
for url in urls:
yield scrapy.Request(url=url, headers=headers, callback=self.parse)
定义parse()方法
parse()方法是Scrapy爬虫中最重要的方法之一。它会接收response对象作为参数,并对获取到的HTML文档进行解析和处理。在这个方法中,可以使用XPath或CSS选择器等工具对文档进行解析,并提取出需要的数据。
XPath选择器 XPath是一种基于XML的查询语言,也可以用于HTML文档的查询。XPath选择器使用XPath表达式来查询匹配的节点,每个节点有多个属性,例如标签名、属性名和文本内容等。 XPath表达式的示例: - 选择节点:
//div # 选择所有div标签节点
- 选择属性:
//div[@class="example"] # 选择class属性值为example的div标签节点
- 选择文本:
//div/text() # 选择div标签节点的文本内容
XPath选择器的常见方法: - xpath(expression) 该方法从当前响应的HTML文档中,选取与XPath表达式相匹配的所有节点。
response.xpath('//title/text()').get()
- extract() 该方法将匹配的节点的文本内容返回为一个字符串。
response.xpath('//title/text()').extract()
css选择器
- 类选择器 使用类选择器可以选取所有具有指定类名的元素。 例如,选取所有class属性为example的div标签:
response.css('div.example')
- ID选择器 ID选择器可以选取具有指定id属性值的元素。 例如,选取id属性为main的div标签:
response.css('div#main')
- 子元素选择器 子元素选择器可以选取指定元素的直接子元素。 例如,选取body标签下的所有div标签:
response.css('body > div')
- 后代元素选择器 后代元素选择器可以选取指定元素下的所有后代元素。 例如,选取body标签下的所有div标签:
response.css('body div')
- 相邻兄弟选择器 相邻兄弟选择器可以选取指定元素后面的紧邻着的兄弟元素。 例如,选取class属性为example的div标签后面的一个p标签:
response.css('div.example + p')
- 通用兄弟选择器 通用兄弟选择器可以选取指定元素后面的所有兄弟元素。 例如,选取class属性为example的div标签后面的所有p标签:
response.css('div.example ~ p')
- 伪类选择器 伪类选择器可以选取元素的特定状态。 例如,选取所有第一行的表格单元格:
response.css('td:first-child')
以上是Scrapy CSS选择器的一些基本示例,可以根据需要进一步结合不同类型的选择器和属性来进行更具体的元素选取。
注意:
Scrapy框架提供了一个Response对象,其中包含请求URL的HTML响应。Response对象提供了直接选择和提取HTML元素的方法,即response.css()和response.xpath(),因此不需要显式地创建一个Selector对象。这样可以使代码更加简洁和易于阅读。同时,使用response.css()和response.xpath()方法可以直接使用Scrapy的内置选择器,而无需导入Selector对象。因此,直接使用response.css()和response.xpath()方法是更好的做法。
使用
getall()
方法获得元素的文本是Scrapy中推荐的做法。它返回一个包含所有匹配到元素的文本的列表,而不仅仅是第一个匹配到的元素的文本。相比之下,extract()
方法只返回第一个匹配到的元素的文本。使用getall()
方法可以更轻松地处理多个匹配,而无需使用循环或列表解析。此外,从性能角度来看,使用getall()
方法可以更快地提取多个元素的文本,因为它只需要执行一次搜索操作,而不是多次搜索。因此,使用getall()
方法获得元素的文本是更好的做法。
定义其他辅助方法
除了start_requests()和parse()方法之外,还可以在Spider文件中定义其他辅助方法。这些方法可以用于将提取出的数据存储到数据库或文件中,或者实现其他一些爬虫功能。
定义Spider的一些属性
在Scrapy的Spider文件中,还可以定义一些属性,比如name、allowed_domains、start_urls等。这些属性可以用于控制爬虫的行为,比如限制爬取的域名范围、设置起始URL等。
Scrapy的items.py文件定义了在Spider中要提取的数据类型和字段。当Spider解析网页时,它会从中提取数据并将其存储到Item实例中。在items.py文件中,可以定义一个或多个类来表示不同类型的数据,如电影、书籍或新闻。这些类通常是基于Python字典构建的,其中键代表数据字段,值代表字段的值。 Scrapy中的Item类是Python字典的子类,它可以处理不同类型的数据。通过定义一个类来表示一个Item,可以为数据指定字段名称和数据类型,并定义一个默认值。
例如,以下是一个简单的Item类定义:
import scrapy
class MyItem(scrapy.Item):
name = scrapy.Field()
age = scrapy.Field(serializer=int)
address = scrapy.Field()
这个类有三个字段:name、age和address。name和address是由默认值None创建的字符串字段,而age是一个整数字段(由serializer=int参数指定)。在Spider中,可以创建一个MyItem类的实例,并将提取的数据存储在字段中,然后将此实例传回给Scrapy引擎进行处理。例如:
import scrapy
from myproject.items import MyItem
class MySpider(scrapy.Spider):
name = "myspider"
# ...
def parse(self, response):
item = MyItem()
item["name"] = "John Smith"
item["age"] = "30"
item["address"] = "123 Main St."
return item
在此示例中,MySpider类中的parse()方法使用MyItem类的实例来存储提取的数据。通过将字段名称和字段值赋给item字典,可以将数据添加到Item实例中。 总之,items.py文件是将提取的数据保存到Item实例中的地方。定义Item类可以指定数据类型和字段名称,并将其传递给Spider以存储提取的数据。
注意:提取到item的数据可以在管道(Pipeline)将提取的数据进行处理。管道是一组Python类,它们分别处理从Spider中提取的Item实例。在管道中,可以执行各种操作,例如数据清理、验证、转换和持久化。每个管道类都定义一个process_item()方法,该方法接收Item实例并返回Item实例或Raise DropItem异常。
Scrapy中的middlewares.py是用来处理请求和响应的过程的模块,它可以在不同的阶段对请求和响应进行处理和修改,方便开发者进行定制化的处理流程。
Scrapy中的middlewares可以分为下载中间件和爬虫中间件:
下载中间件:主要用于处理请求和响应,如设置代理、处理cookie和设置请求头等操作。
- UserAgentMiddleware:随机设置User-Agent,模拟不同的浏览器和设备访问网站。
- ProxyMiddleware:设置代理IP,用于避免IP被封禁的情况。
- RetryMiddleware:在请求失败时自动重试请求。
- HttpErrorMiddleware:用于处理请求出现HTTP错误的情况,可以设置处理不同类型的HTTP错误的方式。
- CookiesMiddleware:用于自动管理cookies,在爬取需要登录的网站时特别有用。
爬虫中间件:主要用于处理爬虫逻辑,对爬取的数据进行处理和过滤,如数据清洗和去重等操作。
- UserAgentMiddleware:随机设置User-Agent,模拟不同的浏览器和设备访问网站。
- ProxyMiddleware:设置代理IP,用于避免IP被封禁的情况。
- RetryMiddleware:在请求失败时自动重试请求。
- HttpErrorMiddleware:用于处理请求出现HTTP错误的情况,可以设置处理不同类型的HTTP错误的方式。
- CookiesMiddleware:用于自动管理cookies,在爬取需要登录的网站时特别有用。
具体来说,middlewares.py中定义了一些中间件类,每个类都包含了一些方法用于对请求和响应进行处理和修改。其中一些常用的方法包括:
通过在middlewares.py中定义自己的中间件类,并在settings.py中进行配置,可以方便地在Scrapy中添加自定义的请求处理流程。
设置User-agent
class RandomUserAgentMiddleWare:
def __init__(self):
self.user_agent_list = [
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36",
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.0.0 Safari/537.36 Edg/113.0.1774.50"]
def process_request(self, request, spider):
request.headers['User-Agent'] = random.choice(self.user_agent_list)
设置代理
class ProxyMiddleWare:
def __init__(self):
self.proxies = {
"http": "http://127.0.0.1:1080",
"https": "https://58.240.110.171:8888"
}
def process_request(self, request, spider):
request.meta['proxy'] = random.choice(list(self.proxies.keys()))
Scrapy的pipelines.py是用于数据处理和数据存储的模块,被用于对爬虫爬取到的数据进行处理和存储。
在Scrapy中,爬虫爬取到的数据是通过Item对象传递到pipelines中进行处理和存储的。pipelines中可以定义多个管道,每个管道对传入的Item对象进行处理,处理完后再传递给下一个管道进行下一步处理。
Scrapy中常用的pipelines包括:
使用Scrapy的pipelines,可以将数据处理和存储分离开来,方便进行代码解耦和灵活性的提升。同时,可以通过设置管道的优先级来决定各个管道的执行顺序,从而实现更加灵活的数据处理和存储需求。
在pipelines.py文件中,可以通过定义一些处理函数来实现数据的处理和存储,同时也可以在setting.py文件中进行管道的设置和优先级的调整。
以下是一个使用管道处理从Spider中提取的MyItem实例的示例:
from myproject.items import MyItem
class MyPipeline:
def process_item(self, item, spider):
# 对item进行处理
item['age'] = int(item['age'])
item['address'] = item['address'].strip()
# 存储item到数据库或文件中
# 这里省略具体实现
return item
在此示例中,MyPipeline类是一个简单的管道类,它将MyItem实例中的数据处理后存储到数据库或文件中。process_item()方法接收item和spider作为参数。在该方法中,可以按照需要对item进行处理,例如将age字段转换为整数并删除address字段的空格。最后,将处理后的item返回,以便它可以传递给下一个管道或Spider的回调函数。 要使用管道,请在配置文件中启用它们。管道的启用顺序也很重要,因为它们按顺序处理Item实例。可以在配置文件中通过ITEM_PIPELINES设置来指定管道及其顺序。例如:
ITEM_PIPELINES = {
'myproject.pipelines.MyPipeline': 300,
# 其他管道类及其顺序
}
在此示例中,MyPipeline类被指定为第一个处理管道,并使用300作为其顺序。顺序从低到高,数字越小的管道首先被处理。配置文件中的其他管道类可以根据需要添加。
Scrapy的setting.py文件包含了许多参数设置,以下是所有参数的详细说明:
使用Scrapy的Settings对象导入配置:我们也可以在Scrapy的Spider类中使用Settings对象来导入配置参数,例如:
from scrapy import Spider
from scrapy.utils.project import get_project_settings
class MySpider(Spider):
name = 'myspider'
def __init__(self, *args, **kwargs):
super(MySpider, self).__init__(*args, **kwargs)
self.settings = get_project_settings()
def start_requests(self):
# 获取参数
download_delay = self.settings.get('DOWNLOAD_DELAY')
# 其他逻辑
这样就可以获取到DOWNLOAD_DELAY配置参数的值。
# Automatically created by: scrapy startproject
#
# For more information about the [deploy] section see:
# https://scrapyd.readthedocs.io/en/latest/deploy.html
[settings]
default = demo.settings
[deploy]
#url = http://localhost:6800/
project = demo
创建项目
scrapy startproject mySpider
创建一个spider
scrapy genspider game 4399.com
编写game.py文件
爬取网页上小游戏的
name :游戏名称
type :分类
time :
image :存储要下载的图片的URL
import scrapy
from scrapy import Request
class GameSpider(scrapy.Spider):
name = "game"
allowed_domains = ["4399.com"]
start_urls = ["https://www.4399.com/flash"]
def url_next(self):
urls = []
for i in range(2, 11):
url = f"https://www.4399.com/flash/new_{i}.htm"
urls.append(url)
return urls
def parse(self, response):
txts = response.xpath('//*[@id="skinbody"]/div[@class="bre oh"]/ul/li')
for _ in txts:
name = _.xpath('a/b/text()').get()
type = _.xpath('em[1]/a/text()').get()
time = _.xpath('em/text()').get()
image = _.xpath('a/img/@lz_src').get()
yield {
'name': name,
'time': time,
'type': type,
'image': image
}
urls = self.url_next()
for url in urls:
yield Request(url, callback=self.parse)
处理下载请求
处理下载请求的工作由Images Pipeline完成。
这是一个Scrapy中的Image Pipeline,用于处理爬虫下载的图片。它继承自Scrapy中的ImagesPipeline类,重写了其中的几个方法。
其中的file_path方法定义了图片文件的保存路径,根据item中的name和type属性生成一个文件名。
item_completed方法在每个图片下载完成后执行,可以在这里对下载的图片进行一些额外的处理,比如打印下载信息、将图片路径保存到item中等。
get_media_requests方法定义了需要下载的图片的请求,这里将item中的image属性作为图片的URL,生成一个Request对象,进行下载。
class ImagePipeline(ImagesPipeline):
def file_path(self, request, response=None, info=None, *, item=None):
name = item['name']
type = item['type']
file_name = f'{type}/{name}.jpg'
return file_name
def item_completed(self, results, item, info):
# image_paths = [x['path'] for ok, x in results if ok]
# if image_paths:
# item['image_paths'] = image_paths
for ok, result in results:
if ok:
path = result['path']
print(f'Downloaded image saved in {path}')
return item
def get_media_requests(self, item, info):
image_paths = "https:" + item['image']
yield scrapy.Request(image_paths)
setting的设置
ITEM_PIPELINES = {
# key是管道的路径
# value是管道的优先级,越小越高
# "demo.pipelines.DemoPipeline": 300,
# "demo.pipelines.newPipeline": 200,
'mySpider.pipelines.ImagePipeline': 299
}
IMAGES_STORE = './images' # 图片保存路径
运行项目
scrapy crawl game