python爬蟲---單線程+多任務的異步協程,selenium爬蟲模塊的使用
一丶單線程+多任務的異步協程
特殊函數
# 若是一個函數的定義被async修飾後,則該函數就是一個特殊的函數
async def get_request(url):
print('正在請求~~', url)
await asyncio.sleep(2)
print('請求結束!!', url)
協程對象
# - 對象: 特殊函數被調用後,函數內部的實現語句不會被當即執行,而後該函數調用會返回一個協程對象。
# - 結論: 協程對象==特殊的函數調用
### c 是 協程對象
# 函數調用: 返回的是一個協程對象
c = get_request(url) # <generator object get_request at 0x000001C626DCF048
任務對象
# 任務對象
- 1.起始就是對協程對象的進一步封裝。
- 2.結論:任務對象==高級的協程對象==特殊的函數調用
- 3.綁定回調:
- 回調函數何時被執行?
- 任務對象執行結束後執行回調函數
- task.add_done_callback(func)
- func必需要有一個參數,該參數表示的是該回調函數對應的任務對象
- 回調函數的參數.result():任務對象對應特殊函數內部的返回值
### 任務對象
task = asyncio.ensure_future(c)
### 綁定回調函數 parase ,傳入函數名便可
task.add_done_callback(parase)
事件循環對象
# 事件循環對象
- 做用:將其內部註冊的任務對象進行異步執行。
### 建立一個事件循環對象
loop = asyncio.get_event_loop()
# 循環執行 , 將任務對象註冊到事件循環對象中而且開啓事件循環
loop.run_until_complete(asyncio.wait(tasks_list))
編碼流程
# 編碼流程
- 定義特殊函數
- 建立協程對象
- 封裝任務對象
- 建立事件循環對象
- 將任務對象註冊到事件循環中且開啓事件循環對象
注意事項***
- 注意:在特殊函數內部的實現語句中不能夠出現不支持異步的模塊對應的代碼,不然
就是終止多任務異步協程的異步效果
- 注意重點:requests模塊不支持異步,在多任務的異步協程中不可使用requests
aiohttp模塊的使用
- 概念:支持異步的網絡請求模塊
- 編碼流程:
- 寫基本架構:
with aiohttp.ClientSession() as s:
with s.get(url) as response:
page_text = response.text()
return page_text
- 補充細節:
- 添加async關鍵字
- 每個with前加上async
- 添加await關鍵字
- 加載每一步的阻塞操做前加上await
- 請求
- 獲取響應數據
# -*-coding:utf-8-*-
# Author:Ds
'''
多任務的異步爬蟲
'''
import asyncio
import time
# aiohttp 異步網絡請求模塊
import aiohttp
from lxml import etree
async def get_request(url):
# 開啓一個鏈接請求
async with aiohttp.ClientSession() as s:
# 發送一個鏈接請求
async with s.get(url=url) as response:
# await 的使用狀況:
# 請求和響應都存在網絡傳輸,
page_text =await response.text()
return page_text
def parase(task):
'''
# 解析page_text
:param task:
:return:
'''
# 獲取 執行函數調用的結果
page_text=task.result()
# 實例化etree解析對象
tree=etree.HTML(page_text)
page_data=tree.xpath('//*[@id="page"]/a[1]/span[1]/i/@class')[0]
print(page_data)
urls = [
'http://127.0.0.1:5000/ip01',
'http://127.0.0.1:5000/ip02',
'http://127.0.0.1:5000/ip01',
'http://127.0.0.1:5000/ip02',
'http://127.0.0.1:5000/ip01',
'http://127.0.0.1:5000/ip02',
]
start_time = time.time()
# 列表 協程對象列表
coroutine_list = []
# 列表 任務對象列表
tasks_list = []
for url in urls:
### c 是 協程對象
# 函數調用: 返回的是一個協程對象
c = get_request(url) # <generator object get_request at 0x000001C626DCF048
### 任務對象
task = asyncio.ensure_future(c)
### 綁定回調函數
task.add_done_callback(parase)
tasks_list.append(task)
coroutine_list.append(c)
# print(coroutine_list)
# print(tasks_list)
### 建立一個事件循環對象
loop = asyncio.get_event_loop()
# 循環執行 , 將任務對象註冊到事件循環對象中而且開啓事件循環
loop.run_until_complete(asyncio.wait(tasks_list))
print('總耗時:', time.time() - start_time)
loop.close()
二丶selenium模塊
概述
- 概念:基於瀏覽器自動化的一個模塊。
- Appium是基於手機的自動化的模塊。
- selenium和爬蟲之間的關聯
- 便捷的爬取到動態加載的數據
- 可見便可得
- 便捷的實現模擬登錄
基本使用
- 基本使用:
- 環境安裝
- pip install selenium
- 下載瀏覽器的驅動程序
- http://chromedriver.storage.googleapis.com/index.html
- 瀏覽器版本和驅動程序的映射關係:
https://blog.csdn.net/huilan_same/article/details/51896672
捕獲動態數據
# -*-coding:utf-8-*-
# Author:Ds
from selenium import webdriver
import time
from lxml import etree
options=webdriver.ChromeOptions()
options.binary_location = r"E:\Program Files (x86)\Google\Chrome\Application\chrome.exe"
browser=webdriver.Chrome('chromedriver.exe',options=options)
url='https://www.fjggfw.gov.cn/Website/JYXXNew.aspx'
browser.get(url)
time.sleep(1)
# page_source : 獲取頁面資源 全部數據
page_text=browser.page_source
# 列表存儲頁面源碼數據
all_page_text=[]
for i in range(3):
next_page_btn=browser.find_element_by_xpath('//*[@id="kkpager"]/div[1]/span[1]/a[7]')
next_page_btn.click()
time.sleep(1)
# 往列表裏添加一整頁數據
all_page_text.append(browser.page_source)
for page_text in all_page_text:
tree=etree.HTML(page_text)
# 獲取頁面指定動態數據的第一條數據
title=tree.xpath('//*[@id="list"]/div[1]/div/h4/a/text()')[0]
print(title)
動做鏈
- 動做鏈
- 在使用find系列的函數進行標籤訂位的時候若是出現了NoSuchElementException如何處理?
- 若是定位的標籤是存在於iframe標籤之下的,則在進行指定標籤訂位的時候
必須使用switch_to.frame()的操做纔可。
# -*-coding:utf-8-*-
# Author:Ds
from selenium import webdriver
import time
from lxml import etree
options=webdriver.ChromeOptions()
options.binary_location = r"E:\Program Files (x86)\Google\Chrome\Application\chrome.exe"
browser=webdriver.Chrome('chromedriver.exe',options=options)
browser.get('https://www.runoob.com/try/try.php?filename=jqueryui-api-droppable')
time.sleep(1)
# 傳入iframe的ID #frame的參數爲iframe標籤的id屬性值
browser.switch_to.frame('iframeResult')
# 找到要執行動做的標籤元素
div_tag=browser.find_element_by_id('draggable')
# 實例化一個動做鏈對象, 傳入瀏覽器對象
action=webdriver.ActionChains(browser)
action.click_and_hold((div_tag))
for i in range(3):
# 執行動做鏈 , perform() 表示當即執行
action.move_by_offset(5,0).perform()
time.sleep(0.1)
browser.quit()
無頭瀏覽器
- 無頭瀏覽器
- phantomjs
- 谷歌無頭瀏覽器(推薦)
# -*-coding:utf-8-*-
# Author:Ds
from selenium.webdriver.chrome.options import Options
from selenium import webdriver
import time
from lxml import etree
# 設置瀏覽器無頭信息
chrome_options=Options()
chrome_options.add_argument('--headless')
chrome_options.add_argument('--disable-gpu')
chrome_options.binary_location=r"E:\Program Files (x86)\Google\Chrome\Application\chrome.exe"
browser=webdriver.Chrome('chromedriver.exe',chrome_options=chrome_options)
browser.get('https://www.taobao.com')
# 截圖保存
browser.save_screenshot('./taobao_index.png')
print(browser.page_source)
如何規避selenium被監測到的風險
- 如何規避selenium被監測到的風險
- 網站能夠根據:'window.navigator.webdriver' 的返回值鑑定是否使用了selenium
- undefind:正常
- true:selenium
# -*-coding:utf-8-*-
# Author:Ds
from selenium.webdriver.chrome.options import Options
from selenium import webdriver
import time
from lxml import etree
# 設置瀏覽器無頭信息
chrome_options=Options()
chrome_options.binary_location=r"E:\Program Files (x86)\Google\Chrome\Application\chrome.exe"
### 設置 不被監測
# console輸入: 'window.navigator.webdriver' 爲true則檢查出是使用的 selenium .
chrome_options.add_experimental_option('excludeSwitches', ['enable-automation'])
browser=webdriver.Chrome('chromedriver.exe',chrome_options=chrome_options)
browser.get('https://www.taobao.com')
三丶12306的模擬登陸
# -*-coding:utf-8-*-
# Author:Ds
# 打碼平臺
from CJY import Chaojiying_Client
# 自動化工具
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from selenium.webdriver import ActionChains
import time
headers={
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36',
}
# 處理圖片模塊
from PIL import Image
# 處理圖片驗證碼
def transform(imgPath,imgType):
chaojiying=Chaojiying_Client('打碼平臺用戶','打碼平臺密碼','901821')
# chaojiying2=Chaojiying_Client('bobo328410948', 'bobo328410948', '899370')
img=open(imgPath,'rb').read()
return chaojiying.PostPic(img,imgType)['pic_str']
# 自動化工具設置
chrome_options=Options()
chrome_options.binary_location=r"E:\Program Files (x86)\Google\Chrome\Application\chrome.exe"
# 生成瀏覽器對象
browser=webdriver.Chrome('chromedriver.exe',chrome_options=chrome_options)
# 發送請求
browser.get('https://kyfw.12306.cn/otn/login/init')
time.sleep(3)
# 截取 整張12306的頁面
browser.save_screenshot('main.png')
# 在main.png 中 截取下載驗證碼
img_tag=browser.find_element_by_xpath('//*[@id="loginForm"]/div/ul[2]/li[4]/div/div/div[3]/img')
# 獲取 驗證碼圖片的位置和大小
location=img_tag.location # 最左下角位置(x,y)
size=img_tag.size # img標籤對應的圖片大小
print(location,size)
# 裁剪範圍
rangle=(int(location['x']),int(location['y']),int(location['x'])+size['width'],int(location['y'])+size['height'])
print(rangle) # 左下座標 和 右上座標
# 裁取驗證碼
i=Image.open('./main.png')
frame=i.crop(rangle)
frame.save('./code.png')
# 解析驗證碼中的內容 , 得到座標
result=transform('./code.png',9004)
print(result)
time.sleep(2)
# 260,140|260,139 ==> [[260,140],[260,139]]
# 構建數據 '266,77|263,168|199,147'
# all_list=[[x] for x in result.split('|')]
# print(all_list)
all_list=[]
for el in result.split('|'):
x,y=el.split(',')
all_list.append([int(x),int(y)])
print(all_list)
# 循環 打碼平臺識別出的圖片座標
for x,y in all_list:
# 執行點擊動做鏈
ActionChains(browser).move_to_element_with_offset(img_tag,x,y).click().perform()
time.sleep(0.5)
# 填寫用戶名
username=browser.find_element_by_xpath('//*[@id="username"]')
username.send_keys('XXXX')
# 填寫用戶密碼
password=browser.find_element_by_xpath('//*[@id="password"]')
password.send_keys('XXXX')
time.sleep(1)
# 點擊登陸
login_btn=browser.find_element_by_xpath('//*[@id="loginSub"]')
login_btn.click()
# 獲取頁面資源,保存到本地
page_text=browser.page_source
with open('./traintest.html','w' ,encoding='utf-8') as f:
f.write(page_text)
time.sleep(8)
# 釋放瀏覽器對象資源
browser.quit()