登录爬虫の趣事

2019-11-24  本文已影响0人  掩流年

title: 登录爬虫の趣事
date: 2019-06-23 17:51:50
tags:


曾经有这么一段斗争

很久很久以前……
第一话
小莫想要某站上所有的电影,写了标准的爬虫(基于HttpClient库),不断地遍历某站的电影列表页面,根据 Html分析电影名字存进自己的数据库。这个站点的运维小黎发现某个时间段请求量陡增,分析日志发现都是IP(xxx.xxx.xxx.xxx)这个用户,并且user-agent还是Python-urllib/2.7,基于这两点判断非人类后直接在服务器上封杀。
第二话
小莫电影只爬了一半,于是也针对性的变换了下策略:

  1. user-agent模仿百度("Baiduspider...")
  2. IP每爬半个小时就换一个IP代理。
    小黎也发现了对应的变化,于是在服务器上设置了一个频率限制,每分钟超过120次请求的再屏蔽IP。
    同时考虑到百度家的爬虫有可能会被误伤,想想市场部门每月几十万的投放,于是写了个脚本,通过 hostname 检查下这个 ip是不是真的百度家的,对这些 ip 设置一个白名单。
    第三话
    小莫发现了新的限制后,想着我也不急着要这些数据,留给服务器慢慢爬吧,于是修改了代码,随机1-3秒爬一次,爬10次休息10秒,每天只在8-12,18-20点爬,隔几天还休息一下。
    小黎看着新的日志头都大了,再设定规则不小心会误伤真实用户,于是准备换了一个思路,当3个小时的总请求超过50次的时候弹出一个验证码弹框,没有正确输入的话就把IP 记录进黑名单。
    第四话
    小莫看到验证码有些傻脸了,不过也不是没有办法,先去学习了图像识别(关键词PIL,tesseract),再对验证码进行了二值化,分词,模式训练之后,总之最后识别了小黎的验证码(关于验证码,验证码的识别,验证码的反识别也是一个恢弘壮丽的斗争史...),之后爬虫又跑了起来。
    第五话
    小黎是个不折不挠的好同学,看到验证码被攻破后,和开发同学商量了变化下开发模式,数据并不再直接渲染,而是由前端同学异步获取,并且通过JavaScript 的加密库生成动态的 token,同时加密库再进行混淆。
    混淆过的加密库就没有办法了么?当然不是,可以慢慢调试,找到加密原理,不过小莫不准备用这么耗时耗力的方法,他放弃了基于HttpClient的爬虫,选择了内置浏览器引擎的爬虫(关键词:PhantomJS,Selenium),在浏览器引擎运行页面,直接获取了正确的结果,又一次拿到了对方的数据。
    第六话
    小黎:……
    故事到这里就结束了,我的故事将此也开始了

1、简单的登录

简洁而有效的代码是令人向往的。

import requests
from urllib.request import urlopen
import xml.etree.ElementTree as ET
def login():
    url = 'http://1.1.1.1/login.action?logout=true'

    data = {
        'login': "Log+in",
        'os_username': 'maskingtime',
        'os_password': '123456',
    }
    response = requests.post(url,data)
    # print(response.text)
    cookie = response.cookies.get_dict()
    print(cookie)
    url2 ="http://1.1.1.1/pages/viewpage.action?pageId=15136981"
    response2 = requests.get(url2,cookies=cookie)
    print(response2.text)
login()

然而……

2.识别的痛苦第一季

关于验证码由不得不说的故事

tesserocr.image_to_text(image)

这样,如果Google爸爸不支持的话,那只能继续这样:

import tesserocr
from PIL import Image

import urllib.request
import pytesseract

url = "http://1.1.1.1/forlogin/img?width=80&height=30&"
dir = '/home/Documents/out_img/ttt.jpg'  # 当前工作目录。
#urllib.request.urlretrieve(url, dir)  # 下载图片。
#image = Image.open('/home/Documents/test.jpg')

def convert_image(imageName):
    img = Image.open(imageName)
    image = img.convert("L")
    threshold = 100  # 设置二值的阈值100
    table = []
    for i in range(256):
        if i < threshold:
            table.append(0)
        else:
            table.append(1)
    image.show()
    image = Image.open(imageName)
    image = image.convert('L')
    image2 = Image.new('L', image.size, 255)
    for x in range(image.size[0]):
        for y in range(image.size[1]):
            pix = image.getpixel((x, y))
            if pix < 120:
                image2.putpixel((x, y), 0)
    return img
image=convert_image(dir)
image.show()
print(pytesseract.image_to_string(image,lang = 'eng'))
print(tesserocr.image_to_text(image))

这样如果还不行的的话,就稍微痛苦一哈,然后进入下一季

3.识别的痛苦第二季

这样怕不怕呢?

import tesserocr
from PIL import Image
import cv2

# 自适应阀值二值化
def _get_dynamic_binary_image(filedir, img_name):
  filename = '/home/Documents/out_img/' + img_name.split('.')[0] + '-binary.jpg'
  print(filename)
  img_name = filedir + '/' + img_name
  print('.....' + img_name)
  im = cv2.imread(img_name)
  im = cv2.cvtColor(im,cv2.COLOR_BGR2GRAY) #灰值化
  # 二值化
  th1 = cv2.adaptiveThreshold(im, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, 21, 1)
  cv2.imwrite(filename,th1)
  return th1

# 干扰线降噪
def interference_line(img, img_name):
  filename = '/home/Documents/out_img/' + img_name.split('.')[0] + '-interferenceline.jpg'
  h, w = img.shape[:2]
  # !!!opencv矩阵点是反的
  # img[1,2] 1:图片的高度,2:图片的宽度
  for y in range(1, w - 1):
    for x in range(1, h - 1):
      count = 0
      if img[x, y - 1] > 245:
        count = count + 1
      if img[x, y + 1] > 245:
        count = count + 1
      if img[x - 1, y] > 245:
        count = count + 1
      if img[x + 1, y] > 245:
        count = count + 1
      if count > 2:
        img[x, y] = 255
  cv2.imwrite(filename,img)
  return img

# 点降噪
def interference_point(img,img_name, x = 0, y = 0):
    """
    9邻域框,以当前点为中心的田字框,黑点个数
    :param x:
    :param y:
    :return:
    """
    filename = '/home/Documents/out_img/' + img_name.split('.')[0] + '-interferencePoint.jpg'
    # todo 判断图片的长宽度下限
    cur_pixel = img[x,y]# 当前像素点的值
    height,width = img.shape[:2]

    for y in range(0, width - 1):
      for x in range(0, height - 1):
        if y == 0:  # 第一行
            if x == 0:  # 左上顶点,4邻域
                # 中心点旁边3个点
                sum = int(cur_pixel) \
                      + int(img[x, y + 1]) \
                      + int(img[x + 1, y]) \
                      + int(img[x + 1, y + 1])
                if sum <= 2 * 245:
                  img[x, y] = 0
            elif x == height - 1:  # 右上顶点
                sum = int(cur_pixel) \
                      + int(img[x, y + 1]) \
                      + int(img[x - 1, y]) \
                      + int(img[x - 1, y + 1])
                if sum <= 2 * 245:
                  img[x, y] = 0
            else:  # 最上非顶点,6邻域
                sum = int(img[x - 1, y]) \
                      + int(img[x - 1, y + 1]) \
                      + int(cur_pixel) \
                      + int(img[x, y + 1]) \
                      + int(img[x + 1, y]) \
                      + int(img[x + 1, y + 1])
                if sum <= 3 * 245:
                  img[x, y] = 0
        elif y == width - 1:  # 最下面一行
            if x == 0:  # 左下顶点
                # 中心点旁边3个点
                sum = int(cur_pixel) \
                      + int(img[x + 1, y]) \
                      + int(img[x + 1, y - 1]) \
                      + int(img[x, y - 1])
                if sum <= 2 * 245:
                  img[x, y] = 0
            elif x == height - 1:  # 右下顶点
                sum = int(cur_pixel) \
                      + int(img[x, y - 1]) \
                      + int(img[x - 1, y]) \
                      + int(img[x - 1, y - 1])

                if sum <= 2 * 245:
                  img[x, y] = 0
            else:  # 最下非顶点,6邻域
                sum = int(cur_pixel) \
                      + int(img[x - 1, y]) \
                      + int(img[x + 1, y]) \
                      + int(img[x, y - 1]) \
                      + int(img[x - 1, y - 1]) \
                      + int(img[x + 1, y - 1])
                if sum <= 3 * 245:
                  img[x, y] = 0
        else:  # y不在边界
            if x == 0:  # 左边非顶点
                sum = int(img[x, y - 1]) \
                      + int(cur_pixel) \
                      + int(img[x, y + 1]) \
                      + int(img[x + 1, y - 1]) \
                      + int(img[x + 1, y]) \
                      + int(img[x + 1, y + 1])

                if sum <= 3 * 245:
                  img[x, y] = 0
            elif x == height - 1:  # 右边非顶点
                sum = int(img[x, y - 1]) \
                      + int(cur_pixel) \
                      + int(img[x, y + 1]) \
                      + int(img[x - 1, y - 1]) \
                      + int(img[x - 1, y]) \
                      + int(img[x - 1, y + 1])

                if sum <= 3 * 245:
                  img[x, y] = 0
            else:  # 具备9领域条件的
                sum = int(img[x - 1, y - 1]) \
                      + int(img[x - 1, y]) \
                      + int(img[x - 1, y + 1]) \
                      + int(img[x, y - 1]) \
                      + int(cur_pixel) \
                      + int(img[x, y + 1]) \
                      + int(img[x + 1, y - 1]) \
                      + int(img[x + 1, y]) \
                      + int(img[x + 1, y + 1])
                if sum <= 4 * 245:
                  img[x, y] = 0
    cv2.imwrite(filename,img)
    return img

dir = '/home/Documents/1.jpg'
image = Image.open(dir)
image.show()

# image=
#image=interference_point(interference_line(_get_dynamic_binary_image('/home/Documents','1.jpg'),'1.jpg'),'1.jpg')
#image=interference_line(interference_point(_get_dynamic_binary_image('/home/Documents','1.jpg'),'1.jpg'),'1.jpg')
image=interference_point(interference_line(interference_point(_get_dynamic_binary_image('/home/Documents/out_img','ttt.jpg'),'ttt.jpg'),'ttt.jpg'),'ttt.jpg')
#print(tesserocr.image_to_text(image))

效果是:


原图
加工后
在加工

可是还是不行呢?稍微痛苦下,然后进入下一季

4.识别的痛苦第三季

此时思路明晰,不为外物所动,由于验证码都是数字,这可简单的很呢!


分类

然后大大说,等等,可以这样干嘞!啊哈,为啥不早说~

5.观赏环节

这位大兄弟写的jsp部分赏析~


观赏1 观赏2

request请求起来这可太折磨人了!

为何不使用jsp?

1.之前就说,java程序员的艺术基因表现形式就一句话,“我觉得挺好看的啊!”所以第一个不是使用jsp的原因我认为是难看!
2.动态资源和静态资源全部耦合在一起,服务器压力大,因为服务器会收到各种http请求,例如css的http请求,js的,图片的等等。一旦服务器出现状况,前后台一起玩完,用户体验极差。
3.如果jsp中的内容很多,页面响应会很慢,因为是同步加载。
4.需要前端工程师使用java的ide,以及需要配置各种后端的开发环境,你们有考虑过前端工程师的感受吗?
以及等等等等

所谓术业有专攻,随着数据量的攀升,jsp这锅乱炖给服务器造成的压力越来越高,维护人员更是叫苦不迭。时代的呼喊,新的框架和设计理念终于把前后端这一块硬石头给劈开了。
离开的原始社会
后端程序员说:
mongodb,http/tcp,多线程,分布式架构(dubbo,dubbox,spring cloud),弹性计算架构,微服务架构(springboot+zookeeper+docker+jenkins),java性能优化。了解一下~
前端程序员说:
html5,css3,jquery,angularjs,bootstrap,reactjs,vuejs,webpack,less/sass,gulp,nodejs,Google V8引擎,javascript多线程,模块化,面向切面编程,设计模式,浏览器兼容性,性能优化了解一下。

舒服了舒服了,题外话截至!

5.使用selenium结束罪恶的一生

import sys
from selenium import webdriver
from bs4 import BeautifulSoup
import time

class login(object):
    def __init__(self, name,usename):
        self.name=name
        self.usename = usename

    def loginPoc(self):
     option = webdriver.ChromeOptions()
     option.add_argument('headless')
     chrome_driver = "/home/Documents/chromedriver"
     browser = webdriver.Chrome(chrome_driver, chrome_options=option)
     # browser1 = webdriver.Chrome(chrome_driver)
     browser.get('http://1.1.1.1/index.jsp')
     try:
        # if lock.acquire(1):
        print(browser.title)
        print(str(self.usename))
        browser.find_element_by_id('u').send_keys(str(self.usename))
        browser.find_element_by_id('p').send_keys('test2019!')
        h1 = browser.current_window_handle
        js = "window.open('1.1.1.1/users/forlogin/')"
        browser.execute_script(js)
        handles = browser.window_handles
        h2 = None
        for handle in handles:
            if handle != h1:
                h2 = handle
        browser.switch_to.window(h2)
        bucket_text = browser.page_source
        soup = BeautifulSoup(bucket_text, "html.parser")
        v = soup.find("challenge").getText()
        browser.switch_to.window(h1)
        browser.find_element_by_id('verifycode').send_keys(v)
        global s_time
        global first
        global fre
        if(first>0):
            s_time = time.time()
            first=0
        browser.find_element_by_id('login_btn').click()

        if ("验证成功" in browser.page_source):
            fre+=1
            print(self.name + " 登录成功")
        else:
            print(self.name + " 登录失败")
        e_time = time.time()

        print("use {:.5}s".format(e_time - s_time))
     finally:
        browser.close()

于是故事就这样结束了~

上一篇下一篇

猜你喜欢

热点阅读