python爬蟲入門 之 單線程 + 多任務異步協程

第五章 .單線程 + 多任務異步協程

5.1 進程和線程

  • 本身寫一個服務端php

    from flask import Flask
    import time

    app = Flask(__name__)

    @app.route('/one')
    def index_one():
       time.sleep(2)
       return  "hello one"

    @app.route('/two')
    def index_two():
       time.sleep(2)
       return  "hello two"

    @app.route('/three')
    def index_three():
       time.sleep(2)
       return  "hello three"

    if __name__ == '__main__':
       app.run(threaded=True)
  • 同步代碼css

    from multiprocessing.dummy import Pool
    import requests
    import time

    start = time.time()
    pool = Pool(3)

    urls = ["http://127.0.0.1:5000/one","http://127.0.0.1:5000/two","http://127.0.0.1:5000/three"]

    for url in urls:
       page_text = requests.get(url,).text
       print(page_text)
    print('總耗時:',time.time()-start)

  • 異步代碼html

    from multiprocessing.dummy import Pool
    import requests
    import time

    start = time.time()
    pool = Pool(3)

    urls = ["http://127.0.0.1:5000/one","http://127.0.0.1:5000/two","http://127.0.0.1:5000/three"]

    def get_request(url):
       return requests.get(url).text

    response_list = pool.map(get_request,urls)
    print(response_list)

    #解析
    def parse(page_text):
       print(len(page_text))

    pool.map(parse,response_list)
    print('總耗時:',time.time()-start)
    print('總耗時:',time.time()-start)

5.2 協程

  • asyncio連接https://www.liaoxuefeng.com/wiki/1016959663602400/1017970488768640python

  • 是一個對象,能夠將協程當作一個特殊的函數,若是一個函數的定義被 anync 關鍵字所修飾,該特殊函數被調用後,函數內部的程序語句不會當即執行 ,而是返回一個協程對象jquery

    from time import sleep

    async def get_request(url):
       print('正在請求:',url)
       sleep(2)
       print('請求結束')

    c = get_request("www.baidu.com")
    print(c)    # <coroutine object get_request at 0x000000000A184308>

5.3任務對象(task)

  • 所謂的任務對象就是對攜程對象的進一步封裝,在任務對象中能夠實現顯示協程對象的運行情況,web

  • 特色正則表達式

    • 任務對象最終是要被註冊到事件循環對象中的.算法

    • 回調函數是綁定給任務對象的,只有當任務對象對應的特殊函數被執行完畢後,回調函數纔會被執行chrome

  • await 掛起的操做,可理解爲交出cpu的使用權json

  • 事件循環對象

    • 無限循環的對象,也能夠把其當成是某一種容器,該容器中須要放入多個任務對象(就是一組待執行的代碼塊)

    • 異步的體現 :當事件循環開始後,該對象會按照順序執行每個任務對象,當一個任務對象發生阻塞時不會等待,而是直接執行下一個任務對象

    from time import sleep
    import asyncio

    #回調函數
    def callback(task):
       print("i am callback")
       print(task.result())    #result 返回的就是任務對象對應的哪一個特殊函數的返回值


    async def get_request(url):
       print('正在請求:',url)
       sleep(2)
       print('請求結束')
       return "hello world"

    #建立一個攜程對象
    c = get_request("www.baidu.com")
    #封裝一個任務對象
    task = asyncio.ensure_future(c)

    #給任務對象綁定回調函數
    task.add_done_callback(callback)

    #建立一個事件循環對象
    loop = asyncio.get_event_loop()
    #將任務對象註冊到事件循環中而且開啓了事件循環
    loop.run_until_complete(task)

5.4多任務異步協程

#注意事項:
1.將多個任務對象存儲到一個列表中,而後將該列表註冊到事件循環中,在註冊的過程當中,該列表須要被wait方法進行處理
2.在任務對象對應的特殊函數內部的實現中,不能夠出現不支持異步模塊的代碼,不然就會中斷整個的異步效果,而且在該函數的每一阻塞的操做都必須使用await關鍵字進行修飾
3.requests模塊對應的代碼不能夠出如今特殊函數內部,由於requests 不支持異步操做
import time
import asyncio

start = time.time()
urls = [
   "http://127.0.0.1:5000/one",
   "http://127.0.0.1:5000/one",
   "http://127.0.0.1:5000/two",
   "http://127.0.0.1:5000/three"
]

#待執行的代碼塊中不能夠出現不支持異步模塊的代碼
#在該函數內部若是有阻塞,阻塞操做必須使用await關鍵字修飾
async def get_request(url):
   print('正在請求:',url)
   # await不用不會報錯,但不會有異步效果
   await asyncio.sleep(2)
   print('請求結束')
   return "hello world"

#放置全部的任務對象
tasks = []
for url in urls:
   c = get_request(url)
   task = asyncio.ensure_future(c)
   tasks.append(task)

loop = asyncio.get_event_loop()
#wait不用會報錯
loop.run_until_complete(asyncio.wait(tasks))
print("所用時長:",time.time()-start)

5.4.1在爬蟲中使用 多任務異步協程

import time
import asyncio
import requests
start = time.time()
urls = [
   "http://127.0.0.1:5000/one",
   "http://127.0.0.1:5000/two",
   "http://127.0.0.1:5000/three"
]


# 沒法實現異步的效果,由於requests 不支持異步操做
async def req(url):
   page_text = await requests.get(url).text
   return page_text


#構建任務列表
tasks = []
for url in urls:
   #協程對象
   c = req(url)
   #任務對象
   task = asyncio.ensure_future(c)
   tasks.append(task)

#建立事件循環對象
loop = asyncio.get_event_loop()
loop.run_until_complete(asyncio.wait(tasks))
print(time.time()-start)

如上圖,咱們會發現出現了一個錯誤,咱們嘗試瞭解決,發現:
#沒法實現異步的效果,由於requests 不支持異步操做
因此咱們引用了了aiohttp模塊

5.4.2 aiohttp模塊

  • 一個支持異步操做的網絡請求的模塊

  • 環境的安裝 pip install aiohttp

  • 記憶步驟:

    第一步: 初步架構

    async def req(url):
    with aiohttp.ClientSession() as s:
    with s.get(url) as response:
    page_text = response.text()
    return page_text

    第二步: 補充細節

    在每個 with 以前加上 async,在每一步阻塞前加上 await

    async def req(url):
       async with aiohttp.ClientSession() as s:
           async with await s.get(url) as response:
               page_text = await response.text()
               return page_text

    server端代碼

    from flask import Flask, render_template
    import time

    app = Flask(__name__)

    @app.route('/one')
    def index_one():
       time.sleep(2)
       return  render_template("text.html")

    @app.route('/two')
    def index_two():
       time.sleep(2)
       return  "hello two"

    @app.route('/three')
    def index_three():
       time.sleep(2)
       return  "hello three"

    if __name__ == '__main__':
       app.run(threaded=True)

    客戶端代碼

    import time
    import asyncio
    import aiohttp
    from lxml import etree

    start = time.time()
    urls = [
       "http://127.0.0.1:5000/one",
       "http://127.0.0.1:5000/two",
       "http://127.0.0.1:5000/three"
    ]


    # 沒法實現異步的效果,由於requests 不支持異步操做
    async def req(url):
       async with aiohttp.ClientSession() as s:
           async with await s.get(url) as response:
               # response.read() 返回 byte 類型的數據
               # response.text() 返回 字符串
               page_text = await response.text()
               return page_text
       # 細節的補充 : 在每個 with 以前加上 async,在每一步阻塞前加上 await


    def parse(task):
       page_text = task.result()
       tree = etree.HTML(page_text)
       name = tree.xpath("//p/text()")[0]
       print(name)

    #構建任務列表
    tasks = []
    for url in urls:
       #協程對象
       c = req(url)
       #任務對象
       task = asyncio.ensure_future(c)

       task.add_done_callback(parse)
       tasks.append(task)

    #建立事件循環對象
    loop = asyncio.get_event_loop()
    loop.run_until_complete(asyncio.wait(tasks))
    print(time.time()-start)

第六章 .圖片懶加載技術、selenium和PhantomJS

6.1圖片懶加載

  • 案例分析:抓取站長素材http://sc.chinaz.com/中的圖片數據

    import requests
    from lxml import etree

    if __name__ == "__main__":
        url = 'http://sc.chinaz.com/tupian/gudianmeinvtupian.html'
        headers = {
            'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36',
        }
        #獲取頁面文本數據
        response = requests.get(url=url,headers=headers)
        response.encoding = 'utf-8'
        page_text = response.text
        #解析頁面數據(獲取頁面中的圖片連接)
        #建立etree對象
        tree = etree.HTML(page_text)
        div_list = tree.xpath('//div[@id="container"]/div')
        #解析獲取圖片地址和圖片的名稱
        for div in div_list:
            image_url = div.xpath('.//img/@src')
            image_name = div.xpath('.//img/@alt')
            print(image_url) #打印圖片連接
            print(image_name)#打印圖片名稱
  • 運行結果觀察發現,咱們能夠獲取圖片的名稱,可是連接獲取的爲空,檢查後發現xpath表達式也沒有問題,究其緣由出在了哪裏呢?

    • 圖片懶加載概念:

      • 圖片懶加載是一種網頁優化技術。圖片做爲一種網絡資源,在被請求時也與普通靜態資源同樣,將佔用網絡資源,而一次性將整個頁面的全部圖片加載完,將大大增長頁面的首屏加載時間。爲了解決這種問題,經過先後端配合,使圖片僅在瀏覽器當前視窗內出現時才加載該圖片,達到減小首屏圖片請求數的技術就被稱爲「圖片懶加載」。

  • 網站通常如何實現圖片懶加載技術呢?

    • 在網頁源碼中,在img標籤中首先會使用一個「僞屬性」(一般使用src2,original......)去存放真正的圖片連接而並不是是直接存放在src屬性中。當圖片出現到頁面的可視化區域中,會動態將僞屬性替換成src屬性,完成圖片的加載。

  • 站長素材案例後續分析:經過細緻觀察頁面的結構後發現,網頁中圖片的連接是存儲在了src2這個僞屬性中

    import requests
    from lxml import etree

    if __name__ == "__main__":
        url = 'http://sc.chinaz.com/tupian/gudianmeinvtupian.html'
        headers = {
            'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36',
        }
        #獲取頁面文本數據
        response = requests.get(url=url,headers=headers)
        response.encoding = 'utf-8'
        page_text = response.text
        #解析頁面數據(獲取頁面中的圖片連接)
        #建立etree對象
        tree = etree.HTML(page_text)
        div_list = tree.xpath('//div[@id="container"]/div')
        #解析獲取圖片地址和圖片的名稱
        for div in div_list:
            image_url = div.xpath('.//img/@src2') #src2僞屬性
            image_name = div.xpath('.//img/@alt')
            print(image_url) #打印圖片連接
            print(image_name)#打印圖片名稱

6.2 selenium

  • 概念: selenium最初是一個自動化測試工具,而爬蟲中使用它主要是爲了解決requests沒法直接執行JavaScript代碼的問題 selenium本質是經過驅動瀏覽器,徹底模擬瀏覽器的操做,好比跳轉、輸入、點擊、下拉等,來拿到網頁渲染以後的結果,可支持多種瀏覽器

  • 環境的安裝 : pip install selenium

  • selenium的演示程序

    from selenium import webdriver
    from time import sleep

    # 後面是你的瀏覽器驅動位置,記得前面加r'','r'是防止字符轉義的
    driver = webdriver.Chrome(r'E:\after homework\day105\chromedriver.exe')
    # 用get打開百度頁面
    driver.get("http://www.baidu.com")
    # 查找頁面的「設置」選項,並進行點擊
    driver.find_elements_by_link_text('設置')[0].click()
    sleep(2)
    # # 打開設置後找到「搜索設置」選項,設置爲每頁顯示50條
    driver.find_elements_by_link_text('搜索設置')[0].click()
    sleep(2)

    # 選中每頁顯示50條
    m = driver.find_element_by_id('nr')
    sleep(2)
    m.find_element_by_xpath('//*[@id="nr"]/option[3]').click()
    m.find_element_by_xpath('.//option[3]').click()
    sleep(2)

    # 點擊保存設置
    driver.find_elements_by_class_name("prefpanelgo")[0].click()
    sleep(2)

    # 處理彈出的警告頁面   肯定accept() 和 取消dismiss()
    driver.switch_to_alert().accept()
    sleep(2)
    # 找到百度的輸入框,並輸入 美女
    driver.find_element_by_id('kw').send_keys('美女')
    sleep(2)
    # 點擊搜索按鈕
    driver.find_element_by_id('su').click()
    sleep(2)
    # 在打開的頁面中找到「Selenium - 開源中國社區」,並打開這個頁面
    driver.find_elements_by_link_text('美女_百度圖片')[0].click()
    sleep(3)

    # 關閉瀏覽器
    driver.quit()
  • selenium和爬蟲之間的關聯?

    1.便捷的獲取頁面中動態加載的數據
    requests模塊進行數據爬取,所見非所得
       selenium所見便可得,但速度較慢
    2.實現模擬登錄
  • 基本操做

  • 瀏覽器建立

    • Selenium支持很是多的瀏覽器,如Chrome、Firefox、Edge等,還有Android、BlackBerry等手機端的瀏覽器。另外,也支持無界面瀏覽器PhantomJS。

      from selenium import webdriver
       
      browser = webdriver.Chrome()
      browser = webdriver.Firefox()
      browser = webdriver.Edge()
      browser = webdriver.PhantomJS()
      browser = webdriver.Safari()
  • 元素定位

    • webdriver 提供了一系列的元素定位方法,經常使用的有如下幾種:

    find_element_by_id()
    find_element_by_name()
    find_element_by_class_name()
    find_element_by_tag_name()
    find_element_by_link_text()
    find_element_by_partial_link_text()
    find_element_by_xpath()
    find_element_by_css_selector()
  • 節點交互

    • Selenium能夠驅動瀏覽器來執行一些操做,也就是說可讓瀏覽器模擬執行一些動做。比較常見的用法有:輸入文字時用send_keys()方法,清空文字時用clear()方法,點擊按鈕時用click()方法。示例以下:

      from selenium import webdriver
      import time

      browser = webdriver.Chrome()
      browser.get('https://www.taobao.com')
      input = browser.find_element_by_id('q')
      input.send_keys('MAC')
      time.sleep(1)
      input.clear()
      input.send_keys('IPhone')
      button = browser.find_element_by_class_name('btn-search')
      button.click()
      browser.quit()
  • 爬取單頁數據

    from selenium import webdriver
    from time import sleep


    #實例化一個瀏覽器對象
    bro = webdriver.Chrome(executable_path=r'E:\after homework\day105\chromedriver.exe')  #executable_path 當前瀏覽器的驅動程序
    url = "https://www.jd.com/"
    #get 用於發起請求
    bro.get(url)
    #定位指定標籤
    search_input = bro.find_element_by_id("key")
    #對指定標籤進行數據交互
    search_input.send_keys("macPro")

    btn = bro.find_element_by_xpath('//*[@id="search"]/div/div[2]/button')
    btn.click()

    sleep(2)
    #執行js代碼
    jsCode = 'window.scrollTo(0,document.body.scrollHeight)'
    bro.execute_script(jsCode)

    sleep(3)
    bro.quit()
  • 爬取多頁數據

    from selenium import webdriver
    from time import sleep
    from lxml import etree


    page_text_list = []
    #實例化一個瀏覽器對象
    bro = webdriver.Chrome(executable_path=r'E:\after homework\day105\chromedriver.exe')  #executable_path 當前瀏覽器的驅動程序
    url = "http://125.35.6.84:81/xk/"
    #get 用於發起請求
    bro.get(url)

    sleep(2)
    #page_source就是當前瀏覽器打開頁面對應的源碼數據
    page_text = bro.page_source
    page_text_list.append(page_text)

    for i in  range(2):
       bro.find_element_by_id('pageIto_next').click()
       sleep(2)
       page_text = bro.page_source
       page_text_list.append(page_text)


    for page_text in page_text_list:
       tree = etree.HTML(page_text)
       li_list = tree.xpath('//*[@id="gzlist"]/li')
       for li in li_list:
           name = li.xpath('./dl/@title')[0]
           print(name)

    sleep(4)
    bro.quit()
  • 動做鏈 :一系列的行爲動做

    地址 https://www.runoob.com/try/try.php?filename=jqueryui-api-droppable

    from selenium import webdriver
    from selenium.webdriver import ActionChains  #動做鏈
    from time import sleep


    page_text_list = []
    #實例化一個瀏覽器對象,executable_path 當前瀏覽器的驅動程序
    bro = webdriver.Chrome(executable_path=r'E:\after homework\day105\chromedriver.exe')
    url = "https://www.runoob.com/try/try.php?filename=jqueryui-api-droppable"

    bro.get(url)

    # 若是定位的標籤是存在於 iframe 對應的子頁面中,在定位標籤前必定要進行 switch_to 的操做
    bro.switch_to.frame('iframeResult')   # iframe標籤的id
    div_tag = bro.find_element_by_id("draggable")
    # 錯誤 :selenium.common.exceptions.NoSuchElementException: Message: no such element: Unable to locate element:

    #實例化動做鏈對象
    action = ActionChains(bro)
    #點擊且長按
    action.click_and_hold(div_tag)

    #模擬人移動算法(本身百度)

    #這裏提供一個簡單操做
    for i in range(5):
       # perform :讓動做鏈當即執行
       action.move_by_offset(17,0).perform()
       sleep(0.5)

    #釋放動做鏈
    action.release()
    sleep(3)
    bro.quit()
  • 執行JavaScript

    • 對於某些操做,Selenium API並無提供。好比,下拉進度條,它能夠直接模擬運行JavaScript,此時使用execute_script()方法便可實現,代碼以下:

    from selenium import webdriver

    browser = webdriver.Chrome()
    browser.get('https://www.jd.com/')
    browser.execute_script('window.scrollTo(0, document.body.scrollHeight)')
    browser.execute_script('alert("123")')
  • 獲取頁面源碼數據

    • 經過page_source屬性能夠獲取網頁的源代碼,接着就可使用解析庫(如正則表達式、Beautiful Soup、pyquery等)來提取信息了

  • 前進和後退

    • 模擬瀏覽器的前進後退

    import time
    from selenium import webdriver

    browser=webdriver.Chrome()
    browser.get('https://www.baidu.com')
    browser.get('https://www.taobao.com')
    browser.get('http://www.sina.com.cn/')

    browser.back()
    time.sleep(10)
    browser.forward()
    browser.close()
  • Cookie處理

    • 使用Selenium,還能夠方便地對Cookies進行操做,例如獲取、添加、刪除Cookies等。示例以下:

      from selenium import webdriver

      browser = webdriver.Chrome()
      browser.get('https://www.zhihu.com/explore')
      print(browser.get_cookies())
      browser.add_cookie({'name': 'name', 'domain': 'www.zhihu.com', 'value': 'germey'})
      print(browser.get_cookies())
      browser.delete_all_cookies()
      print(browser.get_cookies())
  • 異常處理

    from selenium import webdriver
    from selenium.common.exceptions import TimeoutException,NoSuchElementException,NoSuchFrameException

    try:
       browser=webdriver.Chrome()
       browser.get('http://www.runoob.com/try/try.php?filename=jqueryui-api-droppable')
       browser.switch_to.frame('iframssseResult')

    except TimeoutException as e:
       print(e)
    except NoSuchFrameException as e:
       print(e)
    finally:
       browser.close()

6.3 phantomJS

  • PhantomJS是一款無界面的瀏覽器,其自動化操做流程和上述操做谷歌瀏覽器是一致的。因爲是無界面的,爲了可以展現自動化操做流程,PhantomJS爲用戶提供了一個截屏的功能,使用save_screenshot函數實現。

  • 無頭瀏覽器 :無可視化界面的瀏覽器

    • phantomJS

    谷歌無頭瀏覽器

    from selenium import webdriver
    from time import sleep


    from selenium.webdriver.chrome.options import Options

    # 建立一個參數對象,用來控制chrome以無界面模式打開
    chrome_options = Options()
    chrome_options.add_argument('--headless')
    chrome_options.add_argument('--disable-gpu')


    page_text_list = []
    #實例化一個瀏覽器對象,executable_path 當前瀏覽器的驅動程序
    bro = webdriver.Chrome(executable_path=r'E:\after homework\day105\chromedriver.exe',chrome_options=chrome_options)
    bro.get('https://www.baidu.com')

    sleep(2)

    #截圖
    bro.save_screenshot("1.png")

    print(bro.page_source)
    sleep(2)
    bro.quit()
  • selenium規避風險

    可判斷爲正經常使用戶的正常請求

    執行代碼時:

    from selenium import webdriver
    from selenium.webdriver import ChromeOptions
    option = ChromeOptions()
    option.add_experimental_option('excludeSwitches', ['enable-automation'])

    #實例化一個瀏覽器對象
    bro = webdriver.Chrome(executable_path=r'C:\Users\oldboy-python\Desktop\爬蟲+數據\day04\chromedriver.exe',options=option)
    bro.get('https://www.taobao.com/')

登陸qq空間,爬取數據

import requests
from selenium import webdriver
from lxml import etree
import time

driver = webdriver.Chrome(executable_path='/Users/bobo/Desktop/chromedriver')
driver.get('https://qzone.qq.com/')
#在web 應用中常常會遇到frame 嵌套頁面的應用,使用WebDriver 每次只能在一個頁面上識別元素,對於frame 嵌套內的頁面上的元素,直接定位是定位是定位不到的。這個時候就須要經過switch_to_frame()方法將當前定位的主體切換了frame 裏。
driver.switch_to.frame('login_frame')
driver.find_element_by_id('switcher_plogin').click()

#driver.find_element_by_id('u').clear()
driver.find_element_by_id('u').send_keys('328410948')  #這裏填寫你的QQ號
#driver.find_element_by_id('p').clear()
driver.find_element_by_id('p').send_keys('xxxxxx')  #這裏填寫你的QQ密碼
   
driver.find_element_by_id('login_button').click()
time.sleep(2)
driver.execute_script('window.scrollTo(0,document.body.scrollHeight)')
time.sleep(2)
driver.execute_script('window.scrollTo(0,document.body.scrollHeight)')
time.sleep(2)
driver.execute_script('window.scrollTo(0,document.body.scrollHeight)')
time.sleep(2)
page_text = driver.page_source

tree = etree.HTML(page_text)
#執行解析操做
li_list = tree.xpath('//ul[@id="feed_friend_list"]/li')
for li in li_list:
   text_list = li.xpath('.//div[@class="f-info"]//text()|.//div[@class="f-info qz_info_cut"]//text()')
   text = ''.join(text_list)
   print(text+'\n\n\n')
   
driver.close()

爬取豆瓣網中的電影信息

from selenium import webdriver
from time import sleep
import time

if __name__ == '__main__':
   url = 'https://movie.douban.com/typerank?type_name=%E6%81%90%E6%80%96&type=20&interval_id=100:90&action='
   # 發起請求前,可讓url表示的頁面動態加載出更多的數據
   path = r'C:\Users\Administrator\Desktop\爬蟲授課\day05\ziliao\phantomjs-2.1.1-windows\bin\phantomjs.exe'
   # 建立無界面的瀏覽器對象
   bro = webdriver.PhantomJS(path)
   # 發起url請求
   bro.get(url)
   time.sleep(3)
   # 截圖
   bro.save_screenshot('1.png')

   # 執行js代碼(讓滾動條向下偏移n個像素(做用:動態加載了更多的電影信息))
   js = 'window.scrollTo(0,document.body.scrollHeight)'
   bro.execute_script(js)  # 該函數能夠執行一組字符串形式的js代碼
   time.sleep(2)

   bro.execute_script(js)  # 該函數能夠執行一組字符串形式的js代碼
   time.sleep(2)
   bro.save_screenshot('2.png')
   time.sleep(2)
   # 使用爬蟲程序爬去當前url中的內容
   html_source = bro.page_source # 該屬性能夠獲取當前瀏覽器的當前頁的源碼(html)
   with open('./source.html', 'w', encoding='utf-8') as fp:
       fp.write(html_source)
   bro.quit()

6.4 基於selenium實現12306模擬登錄

連接地址 :https://kyfw.12306.cn/otn/login/init

# ChaoJiYing.py
import requests
from hashlib import md5

class Chaojiying_Client(object):

   def __init__(self, username, password, soft_id):
       self.username = username
       password =  password.encode('utf8')
       self.password = md5(password).hexdigest()
       self.soft_id = soft_id
       self.base_params = {
           'user': self.username,
           'pass2': self.password,
           'softid': self.soft_id,
      }
       self.headers = {
           'Connection': 'Keep-Alive',
           'User-Agent': 'Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0)',
      }

   def PostPic(self, im, codetype):
       """
      im: 圖片字節
      codetype: 題目類型 參考 http://www.chaojiying.com/price.html
      """
       params = {
           'codetype': codetype,
      }
       params.update(self.base_params)
       files = {'userfile': ('ccc.jpg', im)}
       r = requests.post('http://upload.chaojiying.net/Upload/Processing.php', data=params, files=files, headers=self.headers)
       return r.json()

   def ReportError(self, im_id):
       """
      im_id:報錯題目的圖片ID
      """
       params = {
           'id': im_id,
      }
       params.update(self.base_params)
       r = requests.post('http://upload.chaojiying.net/Upload/ReportError.php', data=params, headers=self.headers)
       return r.json()
# 12306模擬登錄

from selenium import webdriver
from selenium.webdriver import ActionChains
#Image用做於圖片裁剪
from PIL import Image
from time import sleep
from ChaoJiYing import  Chaojiying_Client


#實例化一個瀏覽器對象,executable_path 當前瀏覽器的驅動程序
bro = webdriver.Chrome(executable_path=r'E:\after homework\day105\chromedriver.exe')
bro.get("https://kyfw.12306.cn/otn/login/init")

sleep(2)
#驗證碼圖片的捕獲
bro.save_screenshot("main.png")
#定位驗證碼圖片對應的標籤
code_img_ele = bro.find_element_by_xpath('//*[@id="loginForm"]/div/ul[2]/li[4]/div/div/div[3]/img')
#驗證碼圖片基於當前整張頁面左上角座標
location = code_img_ele.location
#驗證碼圖片的長和寬
size = code_img_ele.size

#裁剪的矩形區域(左下角和右上角兩點的座標)
rangle = (int(location['x']),int(location['y']),int(location['x']+size['width']),int(location['y']+size['height']))


i = Image.open('main.png')
frame = i.crop(rangle)
frame.save('code.png')

#使用打碼平臺進行驗證碼的識別
chaojiying = Chaojiying_Client('bobo328410948', 'bobo328410948', '899370') #用戶中心>>軟件ID 生成一個替換 96001
im = open('code.png', 'rb').read() #本地圖片文件路徑 來替換 a.jpg 有時WIN系統需要//
result = chaojiying.PostPic(im, 9004)['pic_str']
print(result)  # x1,y1|x2,y2|x3,y3 ==> [[x1,y1],[x2,y2],[x3,y3]]
all_list = []#[[x1,y1],[x2,y2],[x3,y3]] 每個列表元素表示一個點的座標,座標對應值的0,0點是驗證碼圖片左下角
if '|' in result:
   list_1 = result.split('|')
   count_1 = len(list_1)
   for i in range(count_1):
       xy_list = []
       x = int(list_1[i].split(',')[0])
       y = int(list_1[i].split(',')[1])
       xy_list.append(x)
       xy_list.append(y)
       all_list.append(xy_list)
else:
   x = int(result.split(',')[0])
   y = int(result.split(',')[1])
   xy_list = []
   xy_list.append(x)
   xy_list.append(y)
   all_list.append(xy_list)

# action = ActionChains(bro)
for l in all_list:
   x = l[0]
   y = l[1]
   ActionChains(bro).move_to_element_with_offset(code_img_ele,x,y).click().perform()
   sleep(1)

sleep(3)
bro.quit()
相關文章
相關標籤/搜索