Python爬虫实例 动态ip+抓包+验证码自动识别

Posted 7391_KID

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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应该已经过期了。

 

以上是关于Python爬虫实例 动态ip+抓包+验证码自动识别的主要内容,如果未能解决你的问题,请参考以下文章

Python 爬虫高阶

Python验证码识别处理实例(转)

Python验证码识别处理实例(转载)

python爬虫中图形验证码的处理

Python爬虫过程中验证码识别的三种解决方案

Python网络爬虫技巧小总结,静态动态网页轻松爬取数据