第十三章 爬虫
1.写出在网络爬取过程中,遇到防爬问题的解决办法。
2.如何提高爬虫的效率?
- 分布式爬虫
- 开启线程池
- 使用aiohttp异步爬虫
3.你的爬虫爬取的数据量有多少?
4.列举您使用过的 python网络爬虫所用到的模块。
requests,selenium,scrapy,aiohttp
5.简述 requests模块的作用及基本使用?
模拟浏览器发送http,https请求
6.简述 beautifulsoup模块的作用及基本使用
解析html网页,提取,过滤信息
html.parser/find/find_all/text/attrs/get
7.简述 seleninum模块的作用及基本使用?
selenium是用于做web自动化测试的工具,可以将访问站点之间运行在浏览器上面,模拟真实用户操作,爬虫中为了解决JavaScript加载问题而使用他
8.简述 scrapy框架中各组件的工作流程?
ENGINE:引擎,是scrapy框架的核心;内部组件
SCHEDULER:调度器,负责对SPIDER提交的下载请求进行调度;内部组件
DOWNLOADER:下载器,负责下载页面,即发送HTTP请求和接受HTTP响应;内部组件
SPIDER:爬虫,负责从页面解析和提取数据,以及生成新的HTTP请求,用户组件
MIDDLEWARE:中间件,负责对HTTP请求和接受HTTP响应进行处理;可选组件
ITEM PIPELINE:数据管道,负责对爬取的数据进行处理,如去重、写入数据库等
9.在 scrap框架中如何设置代理(两种方法)?
#方式一:内置添加代理功能
# -*- coding: utf-8 -*-
import os
import scrapy
from scrapy.http import Request
class ChoutiSpider(scrapy.Spider):
name = 'chouti'
allowed_domains = ['chouti.com']
start_urls = ['https://dig.chouti.com/']
def start_requests(self):
os.environ['HTTP_PROXY'] = "http://192.168.11.11"
for url in self.start_urls:
yield Request(url=url,callback=self.parse)
def parse(self, response):
print(response)
#方式二:自定义下载中间件
import random
import base64
import six
def to_bytes(text, encoding=None, errors='strict'):
"""Return the binary representation of `text`. If `text`
is already a bytes object, return it as-is."""
if isinstance(text, bytes):
return text
if not isinstance(text, six.string_types):
raise TypeError('to_bytes must receive a unicode, str or bytes '
'object, got %s' % type(text).__name__)
if encoding is None:
encoding = 'utf-8'
return text.encode(encoding, errors)
class MyProxyDownloaderMiddleware(object):
def process_request(self, request, spider):
proxy_list = [
{'ip_port': '111.11.228.75:80', 'user_pass': 'xxx:123'},
{'ip_port': '120.198.243.22:80', 'user_pass': ''},
{'ip_port': '111.8.60.9:8123', 'user_pass': ''},
{'ip_port': '101.71.27.120:80', 'user_pass': ''},
{'ip_port': '122.96.59.104:80', 'user_pass': ''},
{'ip_port': '122.224.249.122:8088', 'user_pass': ''},
]
proxy = random.choice(proxy_list)
if proxy['user_pass'] is not None:
request.meta['proxy'] = to_bytes("http://%s" % proxy['ip_port'])
encoded_user_pass = base64.encodestring(to_bytes(proxy['user_pass']))
request.headers['Proxy-Authorization'] = to_bytes('Basic ' + encoded_user_pass)
else:
request.meta['proxy'] = to_bytes("http://%s" % proxy['ip_port'])
配置:
DOWNLOADER_MIDDLEWARES = {
# 'xiaohan.middlewares.MyProxyDownloaderMiddleware': 543,
}
10.scrapy框架中如何实现大文件的下载?
11.scrapy中如何实现限速?
12.scrapy中如何实现暂停爬虫?
要启用一个爬虫的持久化,运行以下命令:
恢复爬虫用同样的命令
scrapy crawl somespider -s JOBDIR=crawls/somespider-1
命令执行完之后可以安全的停止爬虫
13.scrapy中如何进行自定制命令
#在spiders同级创建任意目录,如:commands
#在其中创建'crawlall.py'文件(此处文件名就是自定义的命令)
from scrapy.commands import ScrapyCommand
from scrapy.utils.project import get_project_settings
class Command(ScrapyCommand):
requires_project = True
def syntax(self):
return '[options]'
def short_desc(self):
return 'Runs all of the spiders'
def run(self, args, opts):
spider_list = self.crawler_process.spiders.list()
for name in spider_list:
self.crawler_process.crawl(name, **opts.__dict__)
self.crawler_process.start()
#在'settings.py'中添加配置'COMMANDS_MODULE = '项目名称.目录名称''
#在项目目录执行命令:'scrapy crawlall'
14.scrapy中如何实现的记录爬虫的深度?
'DepthMiddleware'是一个用于追踪每个Request在被爬取的网站的深度的中间件。
其可以用来限制爬取深度的最大深度或类似的事情。
'DepthMiddleware'可以通过下列设置进行配置(更多内容请参考设置文档):
'DEPTH_LIMIT':爬取所允许的最大深度,如果为0,则没有限制。
'DEPTH_STATS':是否收集爬取状态。
'DEPTH_PRIORITY':是否根据其深度对requet安排优先
15.scrapy中的 pipelines作原理?
Scrapy 提供了 pipeline 模块来执行保存数据的操作。
在创建的 Scrapy 项目中自动创建了一个 pipeline.py 文件,同时创建了一个默认的 Pipeline 类。
我们可以根据需要自定义 Pipeline 类,然后在 settings.py 文件中进行配置即可
16.scrapy的 pipelines如何丢弃一个item对象?
通过raise DropItem()方法
17.简述 scrapy中爬虫中间件和下载中间件的作用?
爬虫中间件
class SpiderMiddleware(object):
def process_spider_input(self,response, spider):
"""
下载完成,执行,然后交给parse处理
:param response:
:param spider:
:return:
"""
pass
def process_spider_output(self,response, result, spider):
"""
spider处理完成,返回时调用
:param response:
:param result:
:param spider:
:return: 必须返回包含 Request 或 Item 对象的可迭代对象(iterable)
"""
return result
def process_spider_exception(self,response, exception, spider):
"""
异常调用
:param response:
:param exception:
:param spider:
:return: None,继续交给后续中间件处理异常;含 Response 或 Item 的可迭代对象(iterable),交给调度器或pipeline
"""
return None
def process_start_requests(self,start_requests, spider):
"""
爬虫启动时调用
:param start_requests:
:param spider:
:return: 包含 Request 对象的可迭代对象
"""
return start_requests
#爬虫中间件
下载中间件
class DownMiddleware1(object):
def process_request(self, request, spider):
"""
请求需要被下载时,经过所有下载器中间件的process_request调用
:param request:
:param spider:
:return:
None,继续后续中间件去下载;
Response对象,停止process_request的执行,开始执行process_response
Request对象,停止中间件的执行,将Request重新调度器
raise IgnoreRequest异常,停止process_request的执行,开始执行process_exception
"""
pass
def process_response(self, request, response, spider):
"""
spider处理完成,返回时调用
:param response:
:param result:
:param spider:
:return:
Response 对象:转交给其他中间件process_response
Request 对象:停止中间件,request会被重新调度下载
raise IgnoreRequest 异常:调用Request.errback
"""
print('response1')
return response
def process_exception(self, request, exception, spider):
"""
当下载处理器(download handler)或 process_request() (下载中间件)抛出异常
:param response:
:param exception:
:param spider:
:return:
None:继续交给后续中间件处理异常;
Response对象:停止后续process_exception方法
Request对象:停止中间件,request将会被重新调用下载
"""
return None
#下载器中间件
18.scrapy-redis组件的作用?
#实现了分布式爬虫,url去重、调度器、数据持久化
'scheduler'调度器
'dupefilter'URL去重规则(被调度器使用)
'pipeline'数据持久化
19.scrapy-redis组件中如何实现的任务的去重?
1. 内部进行配置,连接Redis
2.去重规则通过redis的集合完成,集合的Key为:
key = defaults.DUPEFILTER_KEY % {'timestamp': int(time.time())}
默认配置:
DUPEFILTER_KEY = 'dupefilter:%(timestamp)s'
3.去重规则中将url转换成唯一标示,然后在redis中检查是否已经在集合中存在
from scrapy.utils import request
from scrapy.http import Request
req = Request(url='http://www.cnblogs.com/wupeiqi.html')
result = request.request_fingerprint(req)
print(result) # 8ea4fd67887449313ccc12e5b6b92510cc53675c
scrapy和scrapy-redis的去重规则(源码)
1. scrapy中去重规则是如何实现?
class RFPDupeFilter(BaseDupeFilter):
"""Request Fingerprint duplicates filter"""
def __init__(self, path=None, debug=False):
self.fingerprints = set()
@classmethod
def from_settings(cls, settings):
debug = settings.getbool('DUPEFILTER_DEBUG')
return cls(job_dir(settings), debug)
def request_seen(self, request):
# 将request对象转换成唯一标识。
fp = self.request_fingerprint(request)
# 判断在集合中是否存在,如果存在则返回True,表示已经访问过。
if fp in self.fingerprints:
return True
# 之前未访问过,将url添加到访问记录中。
self.fingerprints.add(fp)
def request_fingerprint(self, request):
return request_fingerprint(request)
2. scrapy-redis中去重规则是如何实现?
class RFPDupeFilter(BaseDupeFilter):
"""Redis-based request duplicates filter.
This class can also be used with default Scrapy's scheduler.
"""
logger = logger
def __init__(self, server, key, debug=False):
# self.server = redis连接
self.server = server
# self.key = dupefilter:123912873234
self.key = key
@classmethod
def from_settings(cls, settings):
# 读取配置,连接redis
server = get_redis_from_settings(settings)
# key = dupefilter:123912873234
key = defaults.DUPEFILTER_KEY % {'timestamp': int(time.time())}
debug = settings.getbool('DUPEFILTER_DEBUG')
return cls(server, key=key, debug=debug)
@classmethod
def from_crawler(cls, crawler):
return cls.from_settings(crawler.settings)
def request_seen(self, request):
fp = self.request_fingerprint(request)
# This returns the number of values added, zero if already exists.
# self.server=redis连接
# 添加到redis集合中:1,添加工程;0,已经存在
added = self.server.sadd(self.key, fp)
return added == 0
def request_fingerprint(self, request):
return request_fingerprint(request)
def close(self, reason=''):
self.clear()
def clear(self):
"""Clears fingerprints data."""
self.server.delete(self.key)