Python爬蟲實例 動態ip+抓包+驗證碼自動識別


  最近出於某種不可描述的原因,需要爬一段數據,大概長這樣:

 

  

 

  是一個價格走勢圖,鼠標移到上面會顯示某個時刻的價格,需要爬下來日期和價格。

  第一步肯定先看源代碼,找到了這樣一段:

  歷史記錄應該是從這個iframe發過來的,點進去看看,找到這樣一段:

  可以大概看出來是通過get一個json文件來獲取數據,我們要的東西應該就在這個json里面。打開瀏覽器的開發者工具(F12),一個個看發過來的json,發現這樣一個:

  

  打開看看:  

 

  ok,我們找到了想要的東西了,接下來分析下這個url,發現一些規律,可以直接從第一頁的url構造出來這個的url,除了一個token...從源代碼里找到這玩意長這樣...

  菜雞如我當然不知所措了...只能模擬瀏覽器抓包了...加載完從這個frame的src里可以找到這個token,問題解決,開爬!

  以上部分的代碼如下:

  

#coding=utf8
import urllib.request
import json
import requests
import re
from selenium import webdriver
import time
from bs4 import BeautifulSoup
import requests
import random
import pytz
import cv2
from matplotlib import pyplot as plt
from PIL import Image, ImageEnhance
import pytesseract
from selenium.webdriver.common.keys import Keys
import sys
import numpy as np
import gc

def get_data_one_page(source, options, page):
    key1 = 'a href="http://tool\.manmanbuy\.com/historyLowest\.aspx?.+" target'
    key2 = 'a href="http://www\.manmanbuy\.com/disSitePro.+" v'
    r = requests.get(source, headers=headers)
    pattern1 = re.compile(key1)
    pattern2 = re.compile(key2)
    html = r.text

    # 通過正則化匹配找到需要的url
    # 這里有多種情況,從源代碼中發現
    url = []
    list1 = re.findall(pattern1, html)
    list2 = re.findall(pattern2, html)
    for i in list1:
        i = i.replace('a href="', '')
        i = i.replace('" target', '')
        url.append(i)
    for i in list2:
        i = i.replace('a href="', '')
        i = i.replace('" v', '')
        url.append(requests.get(i).url)

    cnt = 0
    pattern_token = re.compile('token=.+')
    driver = webdriver.Firefox(firefox_options=options)

    # 設置超時
    driver.set_page_load_timeout(8)

    i = -1
    try_time = 3
    while(i < len(url) - 1):
        i += 1
        this_url = url[i]
        try:
            driver.get(this_url)
        except:
            if try_time:
                i -= 1
                try_time -= 1
            continue

        # get token
        # 找到需要的frame,獲取url,從里面提取token
        ret = driver.find_element_by_id('iframeId').get_attribute('src')
        token = re.findall(pattern_token, ret)
        json_url = this_url.replace('http://tool.manmanbuy.com/historyLowest.aspx?', '')
        json_url = json_url.replace('item.tmall', 'detail.tmall')
        json_url = 'http://tool.manmanbuy.com/history.aspx?DA=1&action=gethistory&' + \
                   json_url + '&bjid=&spbh=&cxid=&zkid=&w=951&' + token[0]

        # 獲取json文件,解析並寫入文件
        try:
            data = requests.get(json_url, proxies=proxy, headers=headers)
            data = json.loads(data.text)
        except:
            if try_time:
                i -= 1
                try_time -= 1
            continue

        if not ('spUrl' in data) or data['spUrl'] == 'https://detail.tmall.com/item.htm?id=544471454551':
            json_url = json_url.replace('detail.tmall', 'item.tmall')
            try:
                data = requests.get(json_url, proxies=proxy, headers=headers)
                data = json.loads(data.text)
            except:
                if try_time:
                    i -= 1
                    try_time -= 1
                continue

        if 'spName' in data:
            print(data['spName'])

        if not ('spUrl' in data) or data['spUrl'] == 'https://detail.tmall.com/item.htm?id=544471454551':
            if try_time:
                i -= 1
                try_time -= 1
            else:
                file = open('data/error_data_' + str(page) + '_' + str(cnt), 'w')
                file.write(json_url+'\n')
                file.write(data['datePrice']+'\n')
                file.close()
            continue
        else:
            file = open('data/data_' + str(page) + '_' + str(cnt), 'w')
            if 'spName' in data:
                file.write(data['spName']+'\n')
            file.write(data['datePrice']+'\n')
            file.close()
        cnt += 1
        try_time = 3

    # 關閉瀏覽器后記得手動釋放內存
    driver.quit()
    del driver
    gc.collect()

def get_data():
    print('firefox start')
    options = webdriver.FirefoxOptions()
    options.set_headless()
    source = 'http://s.manmanbuy.com/Default.aspx?key=%BF%DA%BA%EC&btnSearch=%CB%D1%CB%F7'
    driver = webdriver.Firefox(firefox_options=options)
    driver.get(source)

    print('pass')

    get_data_one_page(source, options, current_page)
    while(current_page <= 1200):
        current_page += 1

        while (1):
            try:
                driver.get(source)
            except:
                time.sleep(2)
                continue
            break

        pagenum = driver.find_element_by_id('pagenum')
        pagenum.send_keys(current_page + 1)
        button = driver.find_element_by_xpath('/html/body/div[1]/div[5]/div[3]/div[5]/input[2]')
        button.click()
        get_data_one_page(source, options, current_page)

    driver.close()


#
if __name__ == '__main__':
    old = sys.stdout
    sys.stdout = open('log', 'r+')
    get_data()

 

  然后就被封ip,出驗證碼 - -

  封ip好說,搞個動態ip,爬幾條換一個,至於ip怎么獲取,花錢買幾個會比較穩定,也有免費提供的,大多數不可用。

  還有一個問題,上面沒有改過header,也可能是被封的原因。

  首先設置header

headers = {'User-Agent': 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:59.0) Gecko/20100101 Firefox/59.0',
           'Accept-Encoding': 'gzip, deflate',
           'Accept- Language': 'en - US, en;q = 0.5'
}

  具體內容可以通過正常訪問去看看應該填什么。

  然后在瀏覽器啟動前加上:

    agent = random.choice(user_agent)
    headers['User-Agent'] = random.choice(agent)
    # ip = ['183.159.82.25', '18118']
    ip = random_ip()
    options.add_argument('user-agent="' + agent + '"')
    options.add_argument('--proxy-server=http://' + ip[0] + ':' + ip[1])

  random_ip就是在可用的ip里隨機取一個。

  還有在requests.get前面加

        proxy = {'https': 'https://' + ip[0] + ':' + ip[1]}

  requests.get改為

data = requests.get(json_url, proxies=proxy, headers=headers)

  就可以更改ip和header,騙過網站啦。當然對一些比較厲害的網站還是不行。。

  

  接下來就是另一個問題了:每爬大概十條后會出現驗證碼,換ip后還在

 

  把它抓下來然后識別出來,send_key給那個框框就行了,至於怎么識別出來呢,顯然這個網站的驗證碼非常naive,就是加了點椒鹽噪點,中值濾波即可,當然圖像增強和二值化是要的,然后丟給pytesseract識別就行了。

注意圖像有邊框,先去掉,當然加個分割效果應該會更好,但是我懶得寫了...

  由於識別准確率有限,認錯了就再來一次,直到過了為止,代碼如下:

def recognize_Captcha(driver):
    try:
        driver.switch_to.frame(driver.find_element_by_id('iframeId'))
        driver.switch_to.frame(driver.find_element_by_id('iframemain'))
        time.sleep(1)
        input_yanzheng = driver.find_element_by_id('txtyz')
        print('find Captcha')
        while (1):
            img_src = driver.find_element_by_id('ImageButton3').screenshot_as_png
            # img_gray = img_src.convert('L')
            # img_sharp = ImageEnhance.Contrast(img_gray).enhance(2.0)
            img_sharp = img_src

            # save Captcha
            file_img = open('yanzhengma.png', 'bw+')
            file_img.write(img_src)
            file_img.close()

            img = cv2.imread('yanzhengma.png')
            # get rid of frame
            img = img[1:20, 1:56]

            # get rid of noise
            img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
            img_gray = cv2.medianBlur(img_gray, 3)
            ret, img_binary = cv2.threshold(img_gray, 150, 255, cv2.THRESH_BINARY)
            cv2.imwrite('img_code.bmp', img_binary)

            # recognize
            code = pytesseract.image_to_string(Image.open('img_code.bmp'), lang='eng')
            code = code.replace(' ', '')
            print(code)
            input_yanzheng.send_keys(code)
            time.sleep(2)
            driver.find_element_by_name('yanzheng').send_keys(Keys.ENTER)
            time.sleep(2)
            try:
                input_yanzheng = driver.find_element_by_id('txtyz')
            except:
                driver.switch_to.default_content()
                break

    except:
        driver.switch_to.default_content()

  

  至此,全部完成,可以開始爬了,boss說要2w條,慢慢爬唄...

  坑爹的事情來了!!這個網站,從第十頁往后,全部是一樣的!!!也就是根本沒有那么多記錄!!

  - - 白寫了...

  完整代碼在我的github上面:https://github.com/KID-7391/crawler_Captcha ,里面的ip應該已經過期了。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM