美文网首页
登录爬虫の趣事

登录爬虫の趣事

作者: 掩流年 | 来源:发表于2019-11-24 23:18 被阅读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()
    

    于是故事就这样结束了~

    相关文章

      网友评论

          本文标题:登录爬虫の趣事

          本文链接:https://www.haomeiwen.com/subject/liirwctx.html