Python+Selenium+ChromeDriver之浏览器爬虫入门
展开阅读全文

##爬虫简介

应用场景

搜索引擎;

网站迁移;

数据整理、分析、统计;

跨域、第三方API调用;

还有,嗯钓鱼网站啊; ......

CURL

curl是利用URL语法在命令行方式下工作的开源文件传输工具。它被广泛应用在Unix、多种Linux发行版中,并且有DOS和Win32、Win64下的移植版本

Shell:

curl https://www.lmcjl.com

传统CURL式爬虫过程

模拟浏览器请求-
编程语言内置对象或方法模拟设置cookie,User-Agent,Request Method,Query String甚至跟踪redirect,向服务器发送一个请求
接收、处理数据流-
对服务器响应数据流字符进行解析,或模拟HTML格式转换为程序相应数据类型方便操作
保存数据-
对数据分析后本地持久化存储(CSV,txt,jpg/png,.json文件,DB)

各语言的CURL实现:

PHP:curl_init();
Java:URLConnection;
Python:requests模块;
Node:http模块;

##CURL式爬虫缺点:

无法获取由Js脚本动态生成的网页信息-当今Web技术的大环境下,为了网站的性能、可扩展性、用户友好等方向,几乎所有的Web页面都在使用浏览器脚本动态生成内容,所以如果没有脚本执行环境就没有数据。

Headless浏览器实现页面解析

##什么是Headless browser?

无头浏览器类似于在流行的Web浏览器的环境中提供对网页的自动控制,但通过命令行接口或使用网络通信来执行。它们特别适用于测试网页,因为它们能够以浏览器相同的方式呈现和解释HTML,包括页面布局、颜色、字体选择和JavaScript和Ajax的执行等样式元素,当使用其它测试方法时,这些元素通常是不可用的
引自Wikipedia Headless browser
简单来说就是用浏览器来对目标URL进行HTML解析,CSS渲染,Js执行,借由API甚至可以模拟用户行为(鼠标点击,键盘输入),但不提供UI渲染。

通过以上我们可以知道Headless browser就是我们想要的爬虫方式了,本文旨在探讨网页爬虫故不对其它用途特性做过多说明。

##常用Headless browser

Phantomjs(项目暂封存,慎用);
Chrome;
FireFox;

##环境

Python + Selenium + ChromeDriver

##解释

###Python

语法简单,有各种成熟扩展库,爬虫周边的库可以轻易找到

###Selenium

Selenium-主要是为了自动化测试Web应用程序,但不局限于此- 一句话解释:控制浏览器,做任何想做的事情。(鼠标点击、拖拽;用户输入,表单填充;History and Location API;Cookie;弹出对话框;定位和操作UI元素,执行JavaScript脚本)因此获取通过ajax渲染的页面的数据就不是什么难事了:)

附 Documentation

###ChromeDriver

如上所述,Selenium可以操作浏览器,但是需要借助ChromeDriver它是实现 WebDriver 有线协议的一个开源工具,它提供了导航到网页、用户输入、JavaScript执行等等的能力。ChromeDriver通过chrome的自动代理框架控制浏览器

##安装

安装 Python -
下载并配置好环境变量,shell输入:python -V 出现对应版本号即安装成功!
安装pip(Python包管理工具)-
python默认自带 pip 在安装目录的scripts目录下,自行配置至环境变量即可,配置好后shell输入:pip -V 出现对应版本号即安装成功!
安装selenium-
shell输入:pip install selenium 提示:Successfully installed selenium-即安装成功!
安装ChromeDriver-(地址:https://chromedriver.storage.googleapis.com/index.html)
下载ChromeDriver,注意版本需与浏览器版本对应,附:版本号对应描述(64位浏览器下载32位即可),下载后与chrome安装目录放在一起,然后配置至环境变量即可,配置好后shell输入:chromedriver 无错误即安装成功!
安装python IDE pyCharm

##使用

百度搜索关键字“Python”

  1. 实例化Driver并获取页面

文件名:searchPython.py

from selenium import webdriver # 从selenium导入webdriver
driver = webdriver.Chrome()  # Optional argument, if not specified will search path.
driver.get('https://www.baidu.com') # 获取百度页面

Shell:

py searchPython.py
  1. 获取输入框元素:
input = driver.find_element_by_id('kw') #获取输入框
searchButton = driver.find_element_by_id('su') #获取搜索按钮

常用查找元素方法:

find_element_by_id # ID
find_elements_by_class_name # class
find_elements_by_tag_name # 标签名
find_elements_by_name # name
find_elements_by_link_text # a标签中的text查找(精确匹配)
find_elements_by_partial_link_text #a标签中的text查找(部分匹配即可)
find_elements_by_css_selector # css选择器查找
find_elements_by_xpath # find_elements_by_xpath("//input"),请翻阅文档

详细链接:https://www.cnblogs.com/hanmk/p/8997786.html

查看常用定位元素方法

注:find_element_by_ 将获取 find_elements_by_ 的第一个元素,获取到的元素会被包装为 WebElement 对象 以上定位元素方法还可以通过如下方式实现:

from selenium.webdriver.common.by import By #支持的定位器策略集
driver.find_element(By.ID,'kw')
driver.find_element(By.ID,'su')
'''注释
策略集如下:
CLASS_NAME = 'class name'
CSS_SELECTOR = 'css selector'
ID = 'id'
LINK_TEXT = 'link text'
NAME = 'name'
PARTIAL_LINK_TEXT = 'partial link text'
TAG_NAME = 'tag name'
XPATH = 'xpath'
'''
  1. 输入并点击
inputElement.send_keys("Python") #输入框输入"Python"
searchButton.click() #搜索

完整代码

from selenium import webdriver # 从selenium导入webdriver

driver = webdriver.Chrome()  # Optional argument, if not specified will search path.
driver.get('https://www.baidu.com') # 获取百度页面
inputElement = driver.find_element_by_id('kw') #获取输入框
searchButton = driver.find_element_by_id('su') #获取搜索按钮
inputElement.send_keys("Python") #输入框输入"Python"
searchButton.click() #搜索

获取 bilibili 网站js动态生成的某栏目标题

这里涉及两个概念: 显式等待(Explicit Waits)- 在给定的条件函数返回True之前,设置一个最长等待时间,和重复执行给定条件函数的间隔时间,如果条件函数在规定时间内返回True则即刻继续执行,否则将抛出一个异常!

隐式等待(Implicit Waits) 告诉WebDriver在试图找到一个元素或元素时,如果它们没有立即可用,则会在一定时间内对DOM进行轮询,默认设置为0,一旦设置,就为WebDevor对象实例的生命周期设置隐式等待。

Explicit Waits

文件名:bilibili.py

from selenium import webdriver
from selenium.webdriver.support.wait import WebDriverWait # 用于实例化一个Driver的显式等待
from selenium.webdriver.common.by import By # 内置定位器策略集
from selenium.webdriver.support import expected_conditions as EC # 内置预期条件函数,具体API请参考此小节后API链接

driver = webdriver.Chrome()
driver.get('https://www.bilibili.com/v/game/esports/?spm_id_from=333.334.primary_menu.35#/9222')
try:
    WebDriverWait(driver, 20, 0.5).until(EC.presence_of_all_elements_located((By.CLASS_NAME,'vd-list'))) #使用expected_conditions自带验证函数
    for doctorName in driver.find_elements_by_css_selector('.vd-list li'):
        print(doctorName.find_element_by_css_selector('.r > a').text)
finally:
    driver.close() # close the driver

WebDriverWait

构造器,接受一个Driver实例,超时时长(秒),执行条件函数的间隔时间。用于实例化一个Driver的显式等待 方法:

1.until(method, message='')

调用第一个参数给定的函数,直到函数不返回False,除了可以接受expected_conditions内置预期条件函数还可以自定义函数,如下

例:

def hasDoctors(d): # 自定义条件函数 when the function is called , the first prms will be a driver
if (len(d.find_elements_by_css_selector('.vd-list li'))):
    return True
return False
WebDriverWait(driver, 20, 0.5).until(hasDoctors) # 自定义函数形式

2.until_not(method, message='')

与until相反

expected_conditions
支持的内置的预期条件,简称EC,如下:
title_is:判断当前页面的title是否完全等于(==)预期字符串
title_contains:判断当前页面的title是否包含预期字符串
presence_of_element_located:判断某个元素是否被加到了dom树里,并不代表该元素一定可见
visibility_of_element_located:判断某个元素是否可见,可见代表元素非隐藏,并且元素的宽和高都不等于0
visibility_of:与上述的方法一样,区别是上述方法要传入元祖locator即(By.ID,'kw'),此方法直接传定位到的WebElement
presence_of_all_elements_located:判断是否至少有1个元素存在于dom树中,有则返回WebElements列表
text_to_be_present_in_element:判断元素的text是否包含预期字符串
text_to_be_present_in_element_value:判断元素的value属性是否包含预期字符串
frame_to_be_available_and_switch_to_it:检查给定帧是否可切换到,如果可以,则将给定的驱动器切换到指定的iframe
......
这里包含了所有的预置条件说明

最终代码一:

def bdsl(request):
  dic = {}
  url = request.GET.get("url",'')
  if url.strip()=='':
    dic['msg'] = 0
    dic['status'] = 1
    return HttpResponse(json.dumps(dic, ensure_ascii=False))
  else:
    driver = webdriver.Chrome()  # Optional argument, if not specified will search path.
    driver.get('https://www.baidu.com') # 获取百度页面
    inputElement = driver.find_element_by_id('kw') #获取输入框
    searchButton = driver.find_element_by_id('su') #获取搜索按钮
    inputElement.send_keys(url) #输入框输入"Python"
    inputElement.submit()
    try:
      WebDriverWait(driver,5).until(
          expected_conditions.title_contains(url))
      bsobj = BeautifulSoup(driver.page_source)
      num_text_element = bsobj.find('span', {'class': 'nums_text'})
      dic['msg'] = num_text_element.text
      dic['status'] = 0
      return HttpResponse(json.dumps(dic, ensure_ascii=False))
    finally:
      #关闭浏览器
      driver.close()

最终代码二:

from selenium import webdriver # 从selenium导入webdriver
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions
from bs4 import BeautifulSoup
import re,requests,random
driver = webdriver.Chrome()  # Optional argument, if not specified will search path.
driver.get('https://www.baidu.com') # 获取百度页面
inputElement = driver.find_element_by_id('kw') #获取输入框
searchButton = driver.find_element_by_id('su') #获取搜索按钮

inputElement.send_keys("已婚妇女的宫颈息肉发病*很高。这可能是由于女性分娩或流产过程中操作不当和机械刺激引起的,从而导致子宫颈收缩和病原体侵入子宫颈*。导致产生宫颈息肉病") #输入框输入"Python"
inputElement.submit()
try:
    WebDriverWait(driver,10).until(expected_conditions.title_contains('百度搜索'))
    res_tr = r'<div class="c-abstract">(.*?)</div>'
    m_tr = re.findall(res_tr,driver.page_source)
    is_cz = 0;
    for line in m_tr :
     res_tr2 = re.findall(r'<em.*?>(.*?)</em>',line)
     print(res_tr2)
     for line1 in res_tr2 :
      if len(line1) >= 5 :
         is_cz = is_cz+1
         break;
    print(is_cz)
finally:
    #关闭浏览器
    driver.close()