标签:pat 自定义 word user into chrome turn ams 依次
在 Scrapy 中,要抓取网站的链接配置、抓取逻辑、解析逻辑都是在 Spider 里完成的。Spider 的一些基础属性和基础方法:
下载中间件,位于Scrapy 的 Request 和 Response之间的处理模块。其在整个架构中起作用的位置有以下两个:
Scheduler
调度出队列的 Request发送给 Downloader 下载之前,即可以在Request下载前对其修改。所以,Downloader Middleware
可以用来修改 User-Agent、处理重定向、设置代理、失败重试、设置Cookies等。Scrapy 已经为我们提供了许多的 Downloader Middleware
,如负责失败重试、重定向的中间件,这些下载中间件被 DOWNLOADER_MIDDLEWARES_BASE
变量所定义,这个变量是一个字典,如下所示:
这个字典的键名是下载中间件的名称,键值代表优先级,越小越靠近Scrapy Engine
,越大越靠近Downloader
,
所以越小越先被调用。若我们想将我们自定义的下载中间件加入到项目中,我们可以修改 DOWNLOADER_MIDDLEWARES
变量。
新建一个项目:
scrapy startproject scrapydownloadertest
新建一个 Spider
进入项目根路径,新建一个名为 httpbin 的 Spider:
scrapy genspider httpbin httpbin.org
修改 httpbin.py 如下:
# -*- coding: utf-8 -*-
import scrapy
class HttpbinSpider(scrapy.Spider):
name = 'httpbin'
allowed_domains = ['httpbin.org']
# 修改成get方式的请求
start_urls = ['http://httpbin.org/get']
def parse(self, response):
# 添加一个行日志,输出 response.text,为了观察Scrapy发送的Request信息
self.logger.debug(response.text)
运行此 Spider:
scrapy crawl httpbin
结果中包含以下 Scrapy 发送的Request信息:
2019-02-09 14:16:58 [httpbin] DEBUG: {
"args": {},
"headers": {
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
"Accept-Encoding": "gzip,deflate,br",
"Accept-Language": "en",
"Connection": "close",
"Host": "httpbin.org",
"User-Agent": "Scrapy/1.5.0 (+https://scrapy.org)"
},
观察 user-Agent,这是Scrapy自动为我们设置的,那么怎么修改用户代理呢?
我们可以直接在 settings.py
中添加 USER_AGENT
变量或者通过 Downloader Middleware
的 process_request()
方法来修改。
前者的话很简单,在settings.py 添加内容如下:
USER_AGENT = Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36
一般推荐使用此方法来设置,但是我们如果想要实现随机的 User-Agent,就需要自己实现一个下载中间件类了,在 middlewares.py
里面添加一个 RandomUseAgentMiddleware
类:
import random
class RandomUseAgentMiddleware():
def __init__(self):
self.user_agents = [
'Mozilla/5.0 (Windows; U; MSIE 9.0; Windows NT 9.0; en-US)',
'Mozilla/5.0 (Windows NT6.1) AppleWebKit/537.2 (KHTML, like Gecko) Chrome/22.0.1216.0 Safari/537.2',
'Mozilla/5.0 (X11; Ubuntu; Linux i686; rv:15.0) Gecko/201000101 Firefox/15.0.1'
]
def process_request(self, request, spider):
# 设置成随机取一个代理
request.headers['User-Agent'] = random.choice(self.user_agents)
要使自定义的Downloader Middleware
生效,就需要添加到DOWNLOADER_MIDDLEWARES
变量中,所以在 settings.py
中取消DOWNLOADER_MIDDLEWARES
注释,并设置成如下内容:
DOWNLOADER_MIDDLEWARES = {
'scrapydownloadertest.middlewares.RandomUseAgentMiddleware': 543,
}
然后,重新运行 Spider,观察结果中的用户代理:
2019-02-09 14:52:56 [httpbin] DEBUG: {
"args": {},
"headers": {
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
"Accept-Encoding": "gzip,deflate,br",
"Accept-Language": "en",
"Connection": "close",
"Host": "httpbin.org",
"User-Agent": "Mozilla/5.0 (Windows; U; MSIE 9.0; Windows NT 9.0; en-US)"
},
确实用户代理被成功地修改为随机列表中的一个。
Spider Middleware 是介入到 Scrapy 的 Spider 处理机制的钩子框架。它的作用有:
Spider Middleware 的使用频率不如 Downloader Middleware 高,在这里省略相关介绍。
Item Pipeline 是项目管道,主要功能有以下 4 点:
crawler
对象,我们可以拿到Scrapy的所有核心组件,如全局配置的每个信息,然后创建一个 Pipeline 实例。参数 cls 就是 Class,最后返回一个 Class 实例。目标:以爬取 360 摄影美图(https://image.so.com)为例,来分别实现 MongoDB、MySQL、Image图片存储的三个 Pipeline。分析网页的结构,观察图片是怎么来的。
结论:最开始默认返回 30 张图片,然后当我们鼠标继续向下移动时,会发送 ajax 请求,从返回的 json里面包含了图片的链接,并且也是 30 张图片,唯一变的参数就是 sn,当 sn =30时,返回的是31 - 60号的图片,依次类推。而且测试后发现当 sn = 0 时,返回的是 1-30 号的图片。
新建Scrapy项目 images360
scrapy startproject images360
新建 Spider
cd images360
scrapy genspider images images.so.com
构造请求
定义爬取的页数为 50,在 settings.py 中定义:
MAX_PAGE = 50
在 Spider中定义 start_requests() 方法,用来生成 50 次 请求:
# -*- coding: utf-8 -*-
import scrapy
from urllib.parse import urlencode
from scrapy import Spider, Request
class ImagesSpider(scrapy.Spider):
name = 'images'
allowed_domains = ['images.so.com']
start_urls = ['http://images.so.com/']
def parse(self, response):
pass
def start_requests(self):
data = {'ch': 'photography', 'listtype': 'new', 'temp': '1'}
base_url = 'https://image.so.com/zj?'
for page in range(1, self.settings.get('MAX_PAGE') + 1):
data['sn'] = (page - 1) * 30
# 字典用 urlencode编码成请求参数
params = urlencode(data)
url = base_url + params
yield Request(url=url, callback=self.parse)
在 Settings.py 中,修改 ROBOTSTXT_OBEY 变量,不遵守该网站的 robots.txt
# Obey robots.txt rules
ROBOTSTXT_OBEY = False
运行 Spider 得到如下结果:
提取信息
首先定义一个 Item 类用于存放 image:
items.py
from scrapy import Item, Field
class ImageItem(Item):
collection = table = 'imgaes'
id = Field()
url = Field()
title = Field()
thumb = Field()
改写 Spider:
def parse(self, response):
result = json.loads(response.text)
for image in result.get('list'):
item = ImageItem()
item['id'] = image.get('imageid')
item['url'] = image.get('qhimg_url')
item['title'] = image.get('group_title')
item['thumb']
存储数据
MongoDB
编写一个 MongoPipeline 将数据保存到 MongoDB 中,在 pipelines.py 中添加如下类的实现:
import pymongo
class MongoPipeline(object):
def __init__(self, mongo_uri, mongo_db):
self.mongo_uri = mongo_uri
self.mongo_db = mongo_db
@classmethod
def from_crawler(cls, crawler):
return cls(
mongo_uri = crawler.settings.get('MONGO_URI'),
mongo_db = crawler.settings.get('MONGO_DB')
)
def open_spider(self,spider):
self.client = pymongo.MongoClient(self.mongo_uri)
self.db = self.client[self.mongo_db]
def process_item(self, item, spider):
self.db[item.collection].insert(dict(item))
return item
def close_spider(self, spider):
self.client.close()
在 settings.py 中添加以下两个变量:
MONGO_URI = 'localhost'
MONGO_DB = 'images360'
MySQL
首先创建数据库 images360,SQL语句:
CREATE DATABASE images360 DEFAULT CHARACTER
SET utf8 COLLATE utf8_general_ci
新建一张表,包含id、url、title、thumb 四个字段,SQL语句:
CREATE TABLE images(
id VARCHAR(255) PRIMARY KEY,
url VARCHAR(255) NULL,
title VARCHAR(255) NULL,
thumb VARCHAR(255) NULL
)
实现 MySQLPipeline:
import pymysql
class MysqlPipeline():
def __init__(self, host, database, user, password, port):
self.host = host
self.database = database
self.user = user
self.password = password
self.port = port
@classmethod
def from_crawler(cls, crawler):
return cls(
host = crawler.settings.get('MYSQL_HOST'),
database = crawler.settings.get('MYSQL_DATABASE'),
user = crawler.settings.get('MYSQL_USER'),
password = crawler.settings.get('MYSQL_PASSWORD'),
port = crawler.settings.get('MYSQL_PORT')
)
def open_spider(self, spider):
self.db = pymysql.connect(self.host, self.user, self.password, self.database, charset='utf8', port=self.port)
self.cursor = self.db.cursor()
def close_spider(self, spider):
self.db.close()
def process_item(self, item, spider):
data = dict(item)
keys = ', '.join(data.keys())
values = ', '.join(['%s'] * len(data))
sql = 'insert into %s (%s) values(%s)' % (item.table, keys, values)
self.cursor.execute(sql,tuple(data.values()))
self.db.commit()
return item
添加几项设置在 settings.py 中:
MYSQL_HOST = 'localhost'
MYSQL_DATABASE = 'images360'
MYSQL_USER = 'root'
MYSQL_PASSWORD = '123456'
MYSQL_PORT = 3306
Image Pipeline
Scrapy 提供了专门处理下载的 Pipeline,包括文件下载和图片下载,下载过程同样支持异步和多线程,效率十分高效。
首先要定义存储文件的路径,在 settings.py 中加入以下代码:
IMAGES_STORE = './images'
内置的 ImagesPipeline 不符合我们的需求,我们要自己定义 ImagePipeline 继承 ImagesPipeline :
from scrapy import Request
from scrapy.exceptions import DropItem
from scrapy.pipelines.images import ImagesPipeline
class ImagePipeline(ImagesPipeline):
def file_path(self, request, response=None, info=None):
url = request.url
file_name = url.split('/')[-1] # 最后一部分作为文件名
return file_name
def item_completed(self, results, item, info):
images_paths = [x['path'] for ok, x in results if ok]
if not images_paths:
raise DropItem('Image Download Failed')
return item
def get_media_requests(self, item, info):
yield Request(item['url'])
启用我们定义的三个 Pipeline,修改settings.py, 设置 ITEM_PIPELINES
:
ITEM_PIPELINES = {
'images360.pipelines.ImagePipeline': 300,
'images360.pipelines.MongoPipeline': 301,
'images360.pipelines.MysqlPipeline': 302,
}
注意下调用顺序,首先调用 ImagePipeline 对 Item 做下载后的筛选下载失败的就不需要进行保存到数据库中了。
最后执行程序,爬取。
scrapy crawl images
崔庆才.《Python3 网络爬虫开发实战》
标签:pat 自定义 word user into chrome turn ams 依次
原文地址:https://www.cnblogs.com/yunche/p/10358088.html