首页 > 技术文章 > j、scrapy+selenium

simpleness 2021-03-31 07:25 原文

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、企业信息查询

  • https://www.qichacha.com/g_AH.html

  • 要求:获取全国不同地区的所有企业名称、法人及地理位置

    • 尽可能获取电话
  • 电话信息获取策略

    • 注册用户

    • 登录之后,获取cookie

    • 在下载中间件中,将cookie设置到request中

      # 设置请求的头
      request.headers['User-Agent'] = user_agents.get_ua()
      
      # 设置Cookie
      request.cookies = cookies.get_cookie()
      

      注:request.cookies是dict类型

3.2、西安小吃大众点评信息

  • http://www.dianping.com/xian/food

  • 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、滑块验证

  1. from selenium.webdriver import ActionChains

  2. 查找滑块所在的div和滑块标签

    div = browser.find_element_by_id('nc_1__scale_text')  # 滑块所有的div
    span = browser.find_element_by_id('nc_1_n1z') # 查找滑块的span
    
  3. 构造动作链并执行

    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

  • 思路

    1. 查到当前页面是否出现验证码图片

    2. 出现了验证码图片,将当前可视区域进行截图

      browser.set_window_size(960, 960)#需要根据自己的桌面的分辨率来设置,目的是让验证图片显示在窗口中
      browser.get_screenshot_as_file('code.png')
      
    3. 获取可视区域的x,y坐标

      frameX = int(iframe.location_once_scrolled_into_view['x'])
      frameY = int(iframe.location_once_scrolled_into_view['y'])
      
    4. 获取验证码的宽和高

      frameWidth = iframe.size['width']
      frameHeight = iframe.size['height']
      
    5. 通过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')
      

推荐阅读