初识Scrapy和使用
了解Scrapy
Scrapy
是一个为了爬取网站数据,提取结构性数据而编写的应用框架。 可以应用在包括数据挖掘,信息处理 或存储历史数据等一系列的程序中。
安装Scrapy
使用pip直接安装
Windows
:打开cmd
,输入 pip install scrapy
,回车。
pip install scrapy
使用清华镜像
pip install -i https://pypi.tuna.tsinghua.edu.cn/simple scrapy
其他镜像网址
清华大学镜像:https://pypi.tuna.tsinghua.edu.cn/simple/
阿里云:http://mirrors.aliyun.com/pypi/simple/
中科大镜像:https://pypi.mirrors.ustc.edu.cn/simple/
创建Scrapy项目
scrapy startproject 项目名称
--- 例如:scrapy startproject spider_demo01
Scrapy项目构成
spiders
__init__.py
自定义的爬虫文件.py ‐‐‐》由我们自己创建,是实现爬虫核心功能的文件
__init__.py
items.py ‐‐‐》定义数据结构的地方,是一个继承自scrapy.Item的类
middlewares.py ‐‐‐》中间件 代理
pipelines.py ‐‐‐》管道文件,里面只有一个类,用于处理下载数据的后续处理
默认是300优先级,值越小优先级越高(1‐1000)
settings.py ‐‐‐》配置文件 比如:是否遵守robots协议,User‐Agent定义等
创建爬虫文件
scrapy genspider 爬虫名字 网页的域名
--- 例如:scrapy genspider baidu https://www.baidu.com
运行爬虫文件
scrapy crawl 爬虫名称
--- 例如:scrapy crawl baidu
Scrapy的架构组成
引擎 ‐‐‐》自动运行,无需关注,会自动组织所有的请求对象,分发给下载器
下载器 ‐‐‐》从引擎处获取到请求对象后,请求数据
spiders
‐‐‐》Spider
类定义了如何爬取某个(或某些)网站。包括了爬取的动作(例 如:是否跟进链接)以及如何从网页的内容中提取结构化数据(爬取item)。 换句话说,Spider
就是您定义爬取的动作及 分析某个网页(或者是有些网页)的地方。调度器 ‐‐‐》有自己的调度规则,无需关注
管道(
Item pipeline
) ‐‐‐》最终处理数据的管道,会预留接口供我们处理数据 当Item
在Spider
中被收集之后,它将会被传递到Item Pipeline
,一些组件会按照一定的顺序执行对Item
的处理。 每个item pipeline
组件(有时称之为“Item Pipeline”
)是实现了简单方法的Python
类。他们接收到Item
并通过它执行 一些行为,同时也决定此Item
是否继续通过pipeline
,或是被丢弃而不再进行处理。
以下是
item pipeline
的一些典型应用:清理
HTML
数据验证爬取的数据(检查item包含某些字段)
查重(并丢弃)
将爬取结果保存到数据库中
Scrapy的工作原理
- 引擎向
spiders
要url
- 引擎将要爬取的url给调度器
- 调度器会将url生成请求对象放入到指定的队列中
- 从队列中出队一个请求
- 引擎将请求交给下载器进行处理
- 下载器发送请求获取互联网数据
- 下载器将数据返回给引擎
- 引擎将数据再次给到
spiders
spiders
通过xpath
解析该数据,得到数据或者url
spiders
将数据或者url
给到引擎- 引|擎判断该数据还是
url
,是数据,交给管道(itempipeline
)处理,是url
交给调度器处理
相关案例
PS:相关案例仅用于学习使用!!!
汽车之家
# 使用前注释robot协议或者改成False
import scrapy
class CarSpider(scrapy.Spider):
name = "car"
allowed_domains = ["cn.58.com"]
start_urls = ["https://cn.58.com/dazhong/?PGTID=0d30001d-008d-2709-b9ed-9477389f1bd7"]
def parse(self, response):
# response.text 字符串
# content = response.text
content = response.xpath("//div[@id='list']//img[@class='info_pic']/@data-original")
for i in content:
print(i.extract())
当当网
# 该文件用于读取一些数据,并未做过多的修饰
import scrapy
from ..items import SpiderDemo03Item
class DangSpider(scrapy.Spider):
name = "dang"
allowed_domains = ["category.dangdang.com", "product.dangdang.com"]
start_urls = ["https://category.dangdang.com/cp01.43.19.00.00.00.html"]
base_url = 'https://category.dangdang.com/pg'
page = 1
def parse(self, response):
li_list = response.xpath("//ul[@id='component_59']/li")
for li in li_list:
# 第一张图片的src可以使用
src = li.xpath(".//img/@data-original").extract_first()
if src:
src = src
else:
src = li.xpath(".//img/@src").extract_first()
name = li.xpath(".//img/@alt").extract_first()
price = li.xpath(".//p[@class='price']/span[@class='search_now_price']/text()").extract_first()
book = SpiderDemo03Item(src=src, name=name, price=price)
# 将数据交给管道文件
yield book
# https://category.dangdang.com/pg2-cp01.43.19.00.00.00.html
# 爬取100页
# if self.page < 100:
# self.page += 1
# url = self.base_url + str(self.page) + '-cp01.43.19.00.00.00.html'
# yield scrapy.Request(url=url, callback=self.parse)
# items.py
class SpiderDemo03Item(scrapy.Item):
# define the fields for your item here like:
# name = scrapy.Field()
src = scrapy.Field()
name = scrapy.Field()
price = scrapy.Field()
time = scrapy.Field()
# pipelines.py
import urllib.request
class SpiderDemo03Pipeline:
# 打开文件
def open_spider(self, spider):
self.fp = open('dang.json', 'w', encoding='utf-8')
def process_item(self, item, spider):
# 写入到json文件
self.fp.write(str(item))
return item
# 关闭文件
def close_spider(self, spider):
self.fp.close()
# 多管道下载
# 记得在当前目录下面添加books文件夹
class dangPipeline:
def process_item(self, item, spider):
url = 'http:'+item.get('src')
filename = './books/' + item.get('name') + '.jpg'
urllib.request.urlretrieve(url, filename)
return item
# settings.py
# 需要添加下图的管道解开注释即可
ITEM_PIPELINES = {
# 下载图片的管道
# "spider_demo03.pipelines.dangPipeline": 301,
# 保存json文件的管道
"spider_demo03.pipelines.SpiderDemo03Pipeline": 300,
}
CrawlSpider
CrawlSpider
可以定义规则,再解析html
内容的时候,可以根据链接规则提取出指定的链接,然后再向这些链接发 送请求
所以,如果有需要跟进链接的需求,意思就是爬取了网页之后,需要提取链接再次爬取,使用CrawlSpider
是非常 合适的
案例:读书网数据入库
创建项目:
scrapy startproject spider_demo
创建爬虫类:
scrapy genspider ‐t crawl read www.dushu.com
配置数据库:
# settings.py
DB_HOST = 'localhost'
DB_PORT = 3306
DB_USER = 'root'
DB_PASSWORD = '123456'
DB_NAME = 'spider'
DB_CHARSET = 'utf8'
# 关闭robot协议
ROBOTSTXT_OBEY = False
# 开启日志输出文件,可以不选择开启
LOG_FILE = "log.log"
编写爬虫类文件:
import scrapy
from scrapy.linkextractors import LinkExtractor
from scrapy.spiders import CrawlSpider, Rule
from ..items import SpiderDemo04Item
class ReadSpider(CrawlSpider):
name = "read"
allowed_domains = ["www.dushu.com"]
start_urls = ["https://www.dushu.com/book/1617_1.html"]
# 这里需要编写匹配的html地址,可以去官网参考
rules = (
Rule(LinkExtractor(
allow=r"/book/1617_\d+"),
callback="parse_item",
follow=True),
)
def parse_item(self, response):
img_list = response.xpath("//div[@class='book-info']//a/img")
for i in img_list:
src = i.xpath("./@data-original").extract_first()
name = i.xpath("./@alt").extract_first()
book = SpiderDemo04Item(name=name, src=src)
yield book
# items.py
class SpiderDemo04Item(scrapy.Item):
# define the fields for your item here like:
name = scrapy.Field()
src = scrapy.Field()
编写管道文件:
# Define your item pipelines here
#
# Don't forget to add your pipeline to the ITEM_PIPELINES setting
# See: https://docs.scrapy.org/en/latest/topics/item-pipeline.html
# useful for handling different item types with a single interface
from itemadapter import ItemAdapter
# 加载配置文件
from scrapy.utils.project import get_project_settings
import pymysql
class SpiderDemo04Pipeline:
def open_spider(self, spider):
self.fp = open('dushu.json', 'w', encoding='utf-8')
def process_item(self, item, spider):
self.fp.write(str(item))
return item
def close_spider(self, spider):
self.fp.close()
class MysqlPipeline:
# 链接MySQL
def open_spider(self, spider):
settings = get_project_settings()
self.conn = pymysql.connect(
host=settings['DB_HOST'],
port=settings['DB_PORT'],
user=settings['DB_USER'],
password=settings['DB_PASSWORD'],
database=settings['DB_NAME'],
charset=settings['DB_CHARSET']
)
self.cursor = self.conn.cursor()
print('链接数据库成功')
def process_item(self, item, spider):
sql = 'insert into book(name,src) values(%s,%s)'
self.cursor.execute(sql, (item['name'], item['src']))
self.conn.commit()
return item
# 关闭链接
def close_spider(self, spider):
self.cursor.close()
self.conn.close()
print('关闭数据库链接')
记得在配置文件打开管道:
ITEM_PIPELINES = {
"spider_demo04.pipelines.MysqlPipeline": 301,
"spider_demo04.pipelines.SpiderDemo04Pipeline": 300,
}
Scrapy的post请求
import scrapy
import json
class DushuSpider(scrapy.Spider):
name = "dushu"
allowed_domains = ["fanyi.baidu.com"]
start_urls = ["https://fanyi.baidu.com/sug/"]
# 需要使用功能 start_requests 方法
def start_requests(self):
url = "https://fanyi.baidu.com/sug/"
data = {
"kw": "hello"
}
yield scrapy.FormRequest(url=url, formdata=data, callback=self.parse_second)
def parse_second(self, response):
content = response.text
obj = json.loads(content)
print(obj)
代理
- 到
settings.py
中,打开一个选项
DOWNLOADER_MIDDLEWARES = {
'postproject.middlewares.Proxy': 543,
}
- 到
middlewares.py
中写代码
def process_request(self, request, spider):
request.meta['proxy'] = 'https://113.68.202.10:9999'
return None