scrapy+selenium
1、基于Selenium自定义下载中间件
1.1、spider
-
常规爬虫(scrapy.Spider)
- 属性或函数
- name
- allowed_domain
- start_urls
- 函数
- parse(self, response)
- start_requests(self)
- 属性或函数
-
规则爬虫(scrapy.spiders.CrawlSpider)
- 属性或函数
- rules
- 属性或函数
1.2、selenium
-
webdriver
-
在spider类的__init__中创建 webbrower
-
webBrower = webdriver.Chrome()
-
options
-
chrome_options
-
selenium.webdriver.chrome.options.Options
-
add_argument('--headless'):处于后台进程运行,即不打开chrome窗口
-
add_argument('--disable-gpu'):禁用gpu加速器
-
-
-
通过webBrower相关操作
-
打开url网页
webBrower.get(url)
-
查找元素
-
提取数据
e=webBrower.find_element_by_id('xx') e.get_attribute('class') e.text
-
发送数据
e.send('xxx')
-
执行点击事件
e.click()
-
-
执行js脚本
execute_script('var q = document.documentElement.scrollTop=100000' )
-
滚动底层-等待UI标签出现
from selenium.webdriver.support import ui, expected_conditions as ec from selenium.webdriver.common.by import By ... # 等待网页中soupager可以选择(可见) ui.WebDriverWait(self.browser, 60).until(ec.visibility_of_all_elements_located((By.CLASS_NAME, 'soupager'))) # 获取页面标签的高度 soupager = self.browser.find_element_by_class_name('soupager') soupager_height = soupager.location['y'] # 向下滚动 # 滚动屏幕到底部 for i in range(20): current_height = (i + 1) * 1000 if current_height >= soupager_height: break self.browser.execute_script('var q = document.documentElement.scrollTop=%s' % current_height) time.sleep(0.5)
-
-
截屏
-
关闭
-
-
1.3、中间件类的三个函数
-
process_request(self, request, spider):当新的请求准备调用下载器下载时,调用的方法
-
process_response(self, request, response, spider):当响应对象,准备返回给engine时,调用的方法
-
process_exception(self, request, exception, spider):当请求过程中出现的异常,如 IgnoreRequest异常,则会调用此方法
1.4、process_request() 方法返回的四种可能
-
return None:继续由下载器下载当前请求对象
-
return Response:不经过下载器下载了,由自己实现下载任务,一般情况下,下载的是html网页,所以封装成HtmlResponse对象
-
return Request:将当前的下载请求封装成一个新的下载请求,并返回给engine,再由engine放入到调度器中, 等待下一次下载
-
raise IgnoreRequest:取消当前的请求,不需要这个请求了,将会转到process_exception(self, request, exception, spider)
2、图片下载管道ImagePipeline
参考文档:https://docs.scrapy.org/en/latest/topics/media-pipeline.html
2.1 核心类与方法
-
from scrapy.pipelines.images import ImagesPipeline
-
def get_media_requests(self, item, info):获取下载图片信息,并发起yield Request()
-
def item_completed(self, results, item, info):item处理完成
results的样本
[(True, {'checksum': '2b00042f7481c7b056c4b410d28f33cf', 'path': 'full/0a79c461a4062ac383dc4fade7bc09f1384a3910.jpg', 'url': 'http://www.example.com/files/product1.pdf'}), (False, Failure(...))]
-
def file_path(self, request, response=None, info=None):获取图片下载文件后保存的路径
2.2、settings.py中的设置
-
ITEM_PIPELINES = {'scrapy.pipelines.images.ImagesPipeline': 1}
-
IMAGES_STORE = '/path/to/valid/dir'
-
图片存放路径
-
要求item必须包含图片的字段
- image_urls = scrapy.Field()
- images = scrapy.Field()
-
也可以指定图片字段名称
- IMAGES_URLS_FIELD = 'field_name_for_your_images_urls'
- IMAGES_RESULT_FIELD = 'field_name_for_your_processed_images'
-
-
IMAGES_EXPIRES = 30
- 图片的有效时间, 单位是 天
-
IMAGES_THUMBS = {'small': (50, 50),'big': (270, 270),}
- 设置缩略图
- 存储的路径是: <IMAGES_STORE>/thumbs/<size_name>/<image_id>.jpg
3、实战
3.1、企业信息查询
-
要求:获取全国不同地区的所有企业名称、法人及地理位置
- 尽可能获取电话
-
电话信息获取策略
-
注册用户
-
登录之后,获取cookie
-
在下载中间件中,将cookie设置到request中
# 设置请求的头 request.headers['User-Agent'] = user_agents.get_ua() # 设置Cookie request.cookies = cookies.get_cookie()
注:request.cookies是dict类型
-
3.2、西安小吃大众点评信息
-
cookies
_lxsdk_cuid=16a4080b834c8-0f5cdd4780c503-4a5469-fa000-16a4080b835c8; _lxsdk=16a4080b834c8-0f5cdd4780c503-4a5469-fa000-16a4080b835c8; _hc.v="\"59047df6-26fb-49e6-9bbf-3e68a53d5016.1555860338\""; cy=17; cye=xian; _lxsdk_s=16a4dbb9bd7-fcb-975-63c%7C%7C118; s_ViewType=10; _dp.ac.v=a1aeeba1-2964-4eea-8bd2-55545b4f2c38; dper=fb841a57876023fffaaea54e52afca889d0d1b9646f531a4e1a180adfb2df65e86463b758c280ffe23f4c6150d2fc5750248a5ff236c0d906024d13fca63e6bcd2c4b967e747fad1c550232c41c20d2ad6767e1a6a70b03659982af14b63af1d; ll=7fd06e815b796be3df069dec7836c3df; ua=dpuser_6569804605; ctu=a4a4e347545841e540c860c34199e51f9dce178bb0dd8aa72132f4013918f353; uamo=17791692095; _lx_utm=utm_source%3DBaidu%26utm_medium%3Dorganic; lgtoken=00c43f810-0073-4d82-9138-86b3e7aa1e91
4、扩展
4.1、滑块验证
-
from selenium.webdriver import ActionChains
-
查找滑块所在的div和滑块标签
div = browser.find_element_by_id('nc_1__scale_text') # 滑块所有的div span = browser.find_element_by_id('nc_1_n1z') # 查找滑块的span
-
构造动作链并执行
action = ActionChains(browser) action.click_and_hold(span).perform() #click_and_hold(span)点击并一直保持,perform()用来执行ActionChains中存储的行为 action.reset_actions() #重新设置动作 action.move_by_offset(div.rect['width'], 0).perform() #移动滑块
优化滑动速度的方法
def get_track(distance): track = [] current = 0 mid = distance * 3 / 4 t = 0.2 v = 0 while current < distance: if current < mid: a = 2 else: a = -3 v0 = v v = v0 + a * t move = v0 * t + 1 / 2 * a * t * t current += move track.append(round(move)) return track for offset in get_track(div.rect['width']): action.move_by_offset(offset, 0).perform() time.sleep(0.01)
4.2、验证码图片截图
参考:https://blog.csdn.net/zwq912318834/article/details/78605486
-
思路
-
查到当前页面是否出现验证码图片
-
出现了验证码图片,将当前可视区域进行截图
browser.set_window_size(960, 960)#需要根据自己的桌面的分辨率来设置,目的是让验证图片显示在窗口中 browser.get_screenshot_as_file('code.png')
-
获取可视区域的x,y坐标
frameX = int(iframe.location_once_scrolled_into_view['x']) frameY = int(iframe.location_once_scrolled_into_view['y'])
-
获取验证码的宽和高
frameWidth = iframe.size['width'] frameHeight = iframe.size['height']
-
通过PIL.Image 裁图
left = frameX top = frameY right = frameX + frameWidth bottom = frameY + frameHeight imgFrame = Image.open('clawerImgs/screenshot.png') imgFrame = imgFrame.crop((left, top, right, bottom)) # 裁剪 imgFrame.save('clawerImgs/iframe.png')
-