Web自动化
有些网站的数据都是通过接口的形式传输或者是一些 JSON 的数据,然后经过 JavaScript 渲染得出来的。用 requests 来爬取内容,爬取下来的只能是服务器端网页的源码,这和浏览器渲染以后的页面内容是不一样的。
真正的数据是经过 JavaScript 执行后,渲染出来的,数据来源可能是 Ajax,也可能是页面里的某些 Data,或者是一些 ifame 页面等。不过,大多数情况下极有可能是 Ajax 接口获取的。因此我们需要分析Ajax请求,分析这些接口的调用方式,通过抓包工具或者浏览器的“开发者工具”,找到数据的请求链接,然后再用程序来模拟。但是,抓包分析流的方式,也存在一定的缺点。因为有些接口带着加密参数,比如 token、sign 等等,模拟难度较大。
Puppeteer、Pyppeteer、Selenium、Splash 等自动化框架出现了。使用这些框架获取HTML源码,这样我们爬取到的源代码就是JavaScript 渲染以后的真正的网页代码,便于数据提取。同时,也就绕过分析 Ajax 和一些 JavaScript 逻辑的过程。这种方式就做到了可见即可爬,难度也不大,同时适合大批量的采集。
selenium
1 pip install selenium -i https://mirrors.aliyun.com/pypi/simple/
这里的版本选择尽量与浏览器版本一致, win64选win32即可,把下载的浏览器驱动放在python解释器所在的文件夹
初识selenium
pip3 install -i https://pypi.tuna.tsinghua.edu.cn/simple selenium
以下是一个最简单的案例,通过模拟键盘输入mac book并通过回车进行搜素
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 from selenium import webdriverfrom selenium.webdriver.common.by import By from selenium.webdriver.common.keys import Keys from selenium.webdriver.support import expected_conditions as ECfrom selenium.webdriver.support.wait import WebDriverWait chrome = webdriver.Chrome() try : chrome.get('https://www.jd.com' ) input_tag = chrome.find_element(By.ID, 'key' ) input_tag.send_keys('mac book' ) input_tag.send_keys(Keys.ENTER) wait = WebDriverWait(chrome, 10 ) wait.until(EC.presence_of_element_located((By.ID, 'J_goodsList' ))) chrome.save_screenshot("hellokitty.png" ) finally : chrome.close()
元素定位
1 el = driver.find_element_by_xxx(value)
1 2 3 from selenium.webdriver.common.by import By driver.find_element(By.xxx,value) driver.find_elements(By.xxx, value)
xxx 处的方式一共有八种
id name class tag link partial xpath css
元素操作
输入文字时用send_keys()
方法,清空文字时用clear()
方法,点击按钮时用click()
方法。示例如下
find_element方法仅仅能够获取元素对象,接下来就可以对元素执行以下操作 从定位到的元素中提取数据的方法
1 2 el.get_attribute(key) el.text
对定位到的元素的操作
1 2 3 4 el.click() el.submit() el.clear() el.send_keys(data)
动作链
以上动作们都是针对某个节点进行操作,例如点击这些,不能实现拖曳等动作,动作连就是为了帮助完成拖曳等连续动作,实现一个节点的拖曳操作,将某个节点从一处拖曳到另外一处。
1 2 3 4 5 6 7 8 9 10 11 12 driver = webdriver.Chrome() dragger = driver.find_element(By.ID, 'dragger' ) item = driver.find_element(By.XPATH, '//div[text()="Item 1"]' ) action = ActionChains(driver) action.drag_and_drop(dragger, item).perform() action.click_and_hold(dragger).release(item2).perform() action.click_and_hold(dragger).move_to_element(item3).release().perform()
执行js
selenium有时候页面上操作无法实现的,这时候就需要借助JS来完成
例如,当页面上的元素超过一屏后,想操作屏幕下方的元素,是不能直接定位到,会报元素不可见的。这时候需要借助滚动条来拖动屏幕,使被操作的元素显示在当前的屏幕上。滚动条是无法直接用定位工具来定位的。selenium里面也没有直接的方法去控制滚动条,这时候只能借助Js代码了,selenium提供了一个操作js的方法:execute_script(),可以直接执行js的脚本。
1 2 3 4 5 6 7 import time from selenium import webdriver browser = webdriver.Chrome() browser.get('https://www.jd.com/') browser.execute_script('window.scrollTo(0, document.body.scrollHeight)') time.sleep(3)
页面等待
如果网站采用了动态html技术,那么页面上的部分元素出现时间便不能确定,这个时候就可以设置一个等待时间,强制等待指定时间,等待结束之后进行元素定位,如果还是无法定位到则报错
页面等待的三种方法
强制等待
也叫线程等待, 通过线程休眠的方式完成的等待,如等待5秒: Thread sleep(5000),一般情况下不太使用强制等待,主要应用的场景在于不同系统交互的地方。
1 2 import timetime.sleep(n)
显式等待
也叫智能等待,针对指定元素定位指定等待时间,在指定时间范围内进行元素查找,找到元素则直接返回,如果在超时还没有找到元素,则抛出异常,显示等待是 selenium 当中比较灵活的一种等待方式,他的实现原理其实是通过 while 循环不停的尝试需要进行的操作。
1 2 3 4 5 6 7 8 9 from selenium.webdriver.common.keys import Keysfrom selenium.webdriver.common.by import Byfrom selenium.webdriver.support.ui import WebDriverWaitfrom selenium.webdriver.support import expected_conditions as EC wait = WebDriverWait(chrome, 10 ,0.5 ) wait.until(EC.presence_of_element_located((By.ID, 'J_goodsList' )))
隐式等待 隐式等待设置之后代码中的所有元素定位都会做隐式等待
通过implicitly Wait完成的延时等待,注意这种是针对全局设置的等待,如设置超时时间为10秒,使用了implicitlyWait后,如果第一次没有找到元素,会在10秒之内不断循环去找元素,如果超过10秒还没有找到,则抛出异常,隐式等待比较智能,它可以通过全局配置,但是只能用于元素定位
1 driver.implicitly_wait(10 )
显示等待和隐式等待的区别 selenium显示等待,就是明确要等到某个元素的出现或者是某个元素的可点击等条件,等不到,就一直等,除非在规定的时间之内都没找到,就会跳出异常Exception。
selenium的隐式等待,就是在创建driver时,为浏览器对象创建一个等待时间,这个方法是得不到某个元素就等待一段时间,直到拿到某个元素位置。在使用隐式等待的时候,实际上浏览器会在你自己设定的时间内部断的刷新页面去寻找我们需要的元素
其他操作
我们写的是爬虫程序,目的是数据,并不需要看网页。如下可设置浏览器后台运行
1 2 3 4 5 6 7 8 9 from selenium.webdriver import Chromefrom selenium.webdriver.chrome.options import Optionsopt = Options() opt.add_argument("--headless" ) opt.add_argument('--disable-gpu' ) opt.add_argument("--window-size=4000,1600" ) web = Chrome(options=opt)
1 2 3 4 5 6 7 8 dictCookies = driver.get_cookies() driver.add_cookie(dictCookies) driver.delete_cookie("CookieName" ) driver.delete_all_cookies()
1 2 3 4 driver.forward() driver.back() driver.refresh() driver.close()
滑动验证案例 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 import timefrom selenium import webdriverfrom selenium.webdriver.common.by import By from selenium.webdriver.support import expected_conditions as ECfrom selenium.webdriver.support.wait import WebDriverWait import cv2from urllib import requestfrom selenium.webdriver.common.action_chains import ActionChainsdef get_distance (): background = cv2.imread("background.png" , 0 ) gap = cv2.imread("gap.png" , 0 ) res = cv2.matchTemplate(background, gap, cv2.TM_CCOEFF_NORMED) value = cv2.minMaxLoc(res)[2 ][0 ] print (value) return value * 278 / 360 def main (): chrome = webdriver.Chrome() chrome.implicitly_wait(5 ) chrome.get('https://passport.jd.com/new/login.aspx?' ) login = chrome.find_element(By.CLASS_NAME, 'login-tab-r' ) login.click() loginname = chrome.find_element(By.ID, 'loginname' ) loginname.send_keys("123@qq.com" ) nloginpwd = chrome.find_element(By.ID, 'nloginpwd' ) nloginpwd.send_keys("987654321" ) loginBtn = chrome.find_element(By.CLASS_NAME, 'login-btn' ) loginBtn.click() img_src = chrome.find_element(By.XPATH, '//*[@class="JDJRV-bigimg"]/img' ).get_attribute("src" ) temp_src = chrome.find_element(By.XPATH, '//*[@class="JDJRV-smallimg"]/img' ).get_attribute("src" ) request.urlretrieve(img_src, "background.png" ) request.urlretrieve(temp_src, "gap.png" ) distance = int (get_distance()) print ("distance:" , distance) print ('第一步,点击滑动按钮' ) element = chrome.find_element(By.CLASS_NAME, 'JDJRV-slide-btn' ) ActionChains(chrome).click_and_hold(on_element=element).perform() ActionChains(chrome).move_by_offset(xoffset=distance, yoffset=0 ).perform() ActionChains(chrome).release(on_element=element).perform() if __name__ == '__main__' : main()
pyppeteer
Selenium流行已久,现在稍微有点反爬的网站都会对selenium和webdriver进行识别,网站只需要在前端js添加一下判断脚本,很容易就可以判断出是真人访问还是webdriver。虽然也可以通过中间代理的方式进行js注入屏蔽webdriver检测,但是webdriver对浏览器的模拟操作(输入、点击等等)都会留下webdriver的标记,同样会被识别出来,要绕过这种检测,只有重新编译webdriver,麻烦自不必说,难度不是一般大。
pyperteer成为了爬虫界的又一新星。相比于selenium具有异步加载、速度快、具备有界面/无界面模式、伪装性更强不易被识别为机器人,同时可以伪装手机平板等终端;虽然支持的浏览器比较单一,但在安装配置的便利性和运行效率方面都要远胜selenium。
pyppeteer是非官方 Python 版本的 Puppeteer 库,浏览器自动化库。 Puppeteer是谷歌出品的一款基于Node.js开发的一款工具,主要是用来操纵Chrome浏览器的 API,通过Javascript代码来操纵Chrome浏览器,完成数据爬取、Web程序自动测试等任务。Puppeteer是 Google 基于 Node.js 开发的工具,调用 Chrome 的 API,通过 JavaScript 代码来操纵 Chrome 完成一些操作,用于网络爬虫、Web 程序自动测试等。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 import randomfrom pyppeteer import launchimport asyncioimport cv2from urllib import requestasync def get_track (): background = cv2.imread("background.png" , 0 ) gap = cv2.imread("gap.png" , 0 ) res = cv2.matchTemplate(background, gap, cv2.TM_CCOEFF_NORMED) value = cv2.minMaxLoc(res)[2 ][0 ] print (value) return value * 278 / 360 async def main (): browser = await launch({ "headless" : False , "args" : ['--window-size=1366,768' ], }) page = await browser.newPage() await page.setViewport({"width" : 1366 , "height" : 768 }) await page.goto("https://passport.jd.com/new/login.aspx?" ) await page.click('div.login-tab-r' ) await page.type ("#loginname" , '123456@qq.com' , { "c" : random.randint(30 , 60 ) }) await page.type ("#nloginpwd" , '1' , { "delay" : random.randint(30 , 60 ) }) await page.waitFor(2000 ) await page.click("div.login-btn" ) await page.waitFor(2000 ) img_src = await page.Jeval(".JDJRV-bigimg > img" , "el=>el.src" ) temp_src = await page.Jeval(".JDJRV-smallimg > img" , "el=>el.src" ) request.urlretrieve(img_src, "background.png" ) request.urlretrieve(temp_src, "gap.png" ) distance = await get_track() """ # Pyppeteer 三种解析方式 Page.querySelector() # 选择器 Page.querySelectorAll() Page.xpath() # xpath 表达式 # 简写方式为: Page.J(), Page.JJ(), and Page.Jx() """ el = await page.J("div.JDJRV-slide-btn" ) box = await el.boundingBox() await page.hover("div.JDJRV-slide-btn" ) await page.mouse.down() await page.mouse.move(box["x" ] + distance + random.uniform(30 , 33 ), box["y" ], {"steps" : 100 }) await page.waitFor(1000 ) await page.mouse.move(box["x" ] + distance + 29 , box["y" ], {"steps" : 100 }) await page.mouse.up() await page.waitFor(2000 ) loop = asyncio.get_event_loop() loop.run_until_complete(main())