crawler爬虫抓取数据

news2024/11/29 2:31:48

crawler爬虫实现

学习目标:
  1. 了解 crawler爬虫运行流程
  2. 了解 crawler爬虫模块实现

1. crawler功能

  1. 初始化driver
  2. 输入公司名称,并点击
  3. 判断是否需要验证
  4. 如果需要验证,获取验证图片并保存
  5. 获取打码坐标
  6. 点击验证图片
  7. 判断查询结果
  8. 选择第一条查询结果
  9. 获取主要信息
  10. 保存数据页面
  11. 向redis中发送信息
  12. 对失败情况进行保存,关闭driver,推送失败信息
  13. 组织抓取逻辑,成功关闭driver

2. crawler代码实现

  • 根据crawler的功能完成函数并组织运行逻辑

/gsxt/crawler.py

......

class GsxtJSCrawler():
    """爬虫"""
    def __init__(self, task_dict={}):
        self.crack_captcha_mode = task_dict.get('crack_captcha_mode', '0') # 打码策略 '0'手动破解;'1'调用打码平台
        self.token = task_dict.get('token', None) # token
        self.company_name = task_dict.get('company_name', None) # 公司名称
        self.proxy = None # 代理ip
        # self.proxy = 'http://182.88.185.38:8123'

        self.headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.87 Safari/537.36'}
        self.url = 'http://www.jsgsj.gov.cn:58888/province/' # 目前只有江苏 重庆等少数地区的接口还算稳定
        # self.url = 'http://www.gsxt.gov.cn/index.html'
        self.captcha_img = None # driver中的图片对象

        self.redis_key = '{}:{}'.format(GSXT_TASK_TOPIC, self.token)

        self.item = {} # 数据


    def init_driver(self):
        """初始化driver"""
        if self.proxy:
            opation = webdriver.ChromeOptions()
            opation.add_argument('--proxy-server={}'.format(self.proxy))
            self.driver = webdriver.Chrome('/home/worker/Desktop/driver/chromedriver', chrome_options=opation)
        else:
            self.driver = webdriver.Chrome('/home/worker/Desktop/driver/chromedriver')
            # self.driver = webdriver.PhantomJS('/home/worker/Desktop/driver/phantomjs')
        self.driver.get(self.url)
        time.sleep(2)
        self.driver.set_window_size(800, 600)


    def send_company_name(self):
        """输入公司名称,并点击"""
        self.driver.find_element_by_xpath('//*[@id="name"]').send_keys(self.company_name)
        time.sleep(1)
        self.driver.find_element_by_xpath('//a[@class="bt-chaxun"]').click()


    def check_captcha_img(self):
        """判断是否需要验证"""
        i = 0
        while i < 3:
            try: # 手动显式等待验证码图片出现
                time.sleep(1)
                # 获取图片对象
                self.captcha_img = self.driver.find_element_by_xpath('//img[@class="geetest_item_img"]')
                return # self.captcha_img != None
            except:
                # 存在不需要验证的情况; 也存在滑动的情况
                # 对于滑动拼图就pass
                if self.driver.current_url != self.url:
                    return # self.captcha_img = None
            i += 1


    def get_captcha_img(self):
        """获取验证图片并保存"""
        captcha_img_url = self.captcha_img.get_attribute('src')
        img_resp = requests.get(captcha_img_url, headers=self.headers)
        img = img_resp.content
        print(img)
        with open('./images/{}.jpg'.format(self.token), 'wb') as f:
            f.write(img)
        """width:100%, height:112%
        使用PIL模块"""
        im = Image.open('./images/{}.jpg'.format(self.token))
        width, height = im.size
        im.thumbnail((width, height / 1.12))
        im.save('./images/{}.jpg'.format(self.token), 'JPEG')


    def get_captcha_offset(self):
        """获取打码坐标"""
        # 手动打码
        if self.crack_captcha_mode == '0':
            i = 0
            while i < 180:
                captcha_offset = redis.hget(self.redis_key, 'captcha_params')
                if captcha_offset is not None:
                    return captcha_offset
                time.sleep(1)
                i += 1
            return None # 超时,没有获取打码坐标

        # 调用第三方打码
        elif self.crack_captcha_mode == '1':
            """暂不实现"""
            return None

        else:
            raise TypeError('仅支持webapi+redis+crawler组件模式的手动或者第三方打码方式')


    def click_captcha_offset(self, captcha_offset_str):
        """点击验证坐标"""
        captcha_offset = []
        # captcha_offset_str = '247,202,142,150,'
        captcha_offset_list = captcha_offset_str.split(',')[:-1]  # ['247', '202', '142', '150']
        for x in captcha_offset_list[::2]:
            y = captcha_offset_str.split(',')[:-1][captcha_offset_str.split(',')[:-1].index(x) + 1]
            captcha_offset.append((x, y))
        # captcha_offset = [('247', '202'), ('142', '150')]
        # captcha_offset = [(x, captcha_offset_str.split(',')[:-1][captcha_offset_str.split(',')[:-1].index(x)+1])
        #                   for x in captcha_offset_str.split(',')[:-1][::2]]
        """点击破解"""
        for i in range(len(captcha_offset)):
            ActionChains(self.driver).move_to_element_with_offset(
                to_element=self.captcha_img,
                xoffset=int(captcha_offset[i][0]) - 0, # 保存的图片和页面上图片大小不一致!
                yoffset=int(captcha_offset[i][1]) - 0).perform()
            # 时间要随机
            time.sleep(1)
            time.sleep(random.random())
            ActionChains(self.driver).click().perform()
        input('注意!这里不光需要模拟真人操作的随机,而且从出现验证图片开始就检测鼠标点击和轨迹!哪怕使用打码平台也要加入无用的鼠标动作!')
        # 点击确认提交
        self.driver.find_element_by_xpath('//a[@class="geetest_commit"]').click()
        time.sleep(2)
        # 判断点击是否成功
        captcha_img = self.driver.find_elements_by_xpath('//img[@class="geetest_item_img"]')
        return False if captcha_img != [] else True # 如果还有验证图片就说明失败了


    def check_result(self):
        """判断查询结果"""
        time.sleep(2)
        rets = self.driver.find_elements_by_xpath('//div[@class="listbox"]')
        return False if rets == [] else True


    def choice_first_result(self):
        """选择第一条查询结果"""
        time.sleep(2)
        self.driver.find_element_by_xpath('//div[@class="listbox"]/a[1]').click()
        """有时会跳出新的标签页,所以根据句柄强行切换到最后一个标签页"""
        self.driver.switch_to.window(self.driver.window_handles[-1])


    def get_baseinfo_item(self):
        """获取主要信息
        """
        i = 0
        while i<5: # 手动显式等待,等待页面加载完毕,以reg_no是否出现为标志
            reg_no = self.driver.find_elements_by_xpath('//*[@id="REG_NO"]')
            if reg_no != []:
                break
            time.sleep(3)
            i += 1
        # 统一社会信用代码/注册号REG_NO
        reg_no = self.driver.find_elements_by_xpath('//*[@id="REG_NO"]')
        if reg_no == []:
            return False
        self.item['reg_no'] = reg_no[0].text if id != [] else ''
        # 企业名称CORP_NAME
        corp_name = self.driver.find_elements_by_xpath('//*[@id="CORP_NAME"]')
        self.item['corp_name'] = corp_name[0].text if id != [] else ''
        # 类型ZJ_ECON_KIND
        zj_econ_kind = self.driver.find_elements_by_xpath('//*[@id="ZJ_ECON_KIND"]')
        self.item['zj_econ_kind'] = zj_econ_kind[0].text if id != [] else ''
        # 法定代表人OPER_MAN_NAME
        oper_man_name = self.driver.find_elements_by_xpath('//*[@id="OPER_MAN_NAME"]')
        self.item['oper_man_name'] = oper_man_name[0].text if id != [] else ''
        # 注册资本REG_CAPI
        reg_cpi = self.driver.find_elements_by_xpath('//*[@id="REG_CAPI"]')
        self.item['reg_cpi'] = reg_cpi[0].text if id != [] else ''
        # 成立日期START_DATE
        start_date = self.driver.find_elements_by_xpath('//*[@id="START_DATE"]')
        self.item['oper_man_name'] = start_date[0].text if id != [] else ''
        # 营业期限自FARE_TERM_START
        fare_term_start = self.driver.find_elements_by_xpath('//*[@id="FARE_TERM_START"]')
        self.item['fare_term_start'] = fare_term_start[0].text if id != [] else ''
        # 营业期限至FARE_TERM_END
        fare_term_end = self.driver.find_elements_by_xpath('//*[@id="FARE_TERM_END"]')
        self.item['fare_term_end'] = fare_term_end[0].text if id != [] else ''
        # 登记机关BELONG_ORG
        belong_org = self.driver.find_elements_by_xpath('//*[@id="BELONG_ORG"]')
        self.item['belong_org'] = belong_org[0].text if id != [] else ''
        # 核准日期CHECK_DATE
        check_date = self.driver.find_elements_by_xpath('//*[@id="CHECK_DATE"]')
        self.item['check_date'] = check_date[0].text if id != [] else ''
        # 登记状态CORP_STATUS
        corp_status = self.driver.find_elements_by_xpath('//*[@id="CORP_STATUS"]')
        self.item['corp_status'] = corp_status[0].text if id != [] else ''
        # 住所ADDR
        addr = self.driver.find_elements_by_xpath('//*[@id="ADDR"]')
        self.item['addr'] = addr[0].text if id != [] else ''
        # 经营范围FARE_SCOPE
        fare_scope = self.driver.find_elements_by_xpath('//*[@id="FARE_SCOPE"]')
        self.item['fare_scope'] = fare_scope[0].text if id != [] else ''
        return True


    def save_html(self):
        """保存首页数据页面,后续可提取完整信息
        同样可以保存其他数据页
        """
        file_name = './html/{}_base.html'.format(self.item['reg_no'])
        with open(file_name, 'w') as f:
            f.write(self.driver.page_source)


    def save_fail(self, msg):
        """保存失败情况,关闭driver,推送失败信息"""
        # self.driver.save_screenshot('./error/{}.png'.format(self.token)) # 70版本的chrome不能调用截图功能
        print(msg)
        file_name = './error/{}_base.html'.format(self.token)
        with open(file_name, 'w') as f:
            f.write(self.driver.page_source)
        self.driver.quit() # 先保存失败,再关闭driver!
        self.send_msg_to_redis(msg=msg, status='failed')


    def send_msg_to_redis(self, msg, status):
        """向redis中发送信息"""
        redis.hset(self.redis_key, 'status', status)
        redis.hset(self.redis_key, 'msg', msg)


    def _main(self):
        """抓取逻辑"""
        if self.company_name is None:
            print('没有公司名称,查个毛线')
            return
        if self.token is None:
            print('想单文件抓取自己写啊!')
            return
        try: # 初始化driver
            self.init_driver()
        except:
            self.save_fail('初始化失败')
            return

        self.send_msg_to_redis(msg='抓取进行中', status='crawling')

        try: # 输入公司名称点击
            self.send_company_name()
        except:
            input(11)
            self.save_fail('输入公司名称点击失败')
            return

        self.check_captcha_img() # 检查是否需要验证
        if self.captcha_img is not None: # 需要验证的逻辑
            self.get_captcha_img() # 获取验证图片并保存
            captcha_offset_str = self.get_captcha_offset() # 获取打码结果
            print(captcha_offset_str)
            ret = self.click_captcha_offset(captcha_offset_str) # 点击验证坐标
            if not ret: # 验证点击失败
                self.save_fail('验证点击失败, 点对了也失败是因为同一ip访问次数过多, 请更换代理ip')
                return
        if not self.check_result(): # 判断 没有结果就结束
            self.save_fail('查询失败')
            return

        """仅对结果列表中第一个搞事情
        拿到所有html的page_source,并只返回主要信息
        提取数据的思路:提取一点就保存一点!
        """
        self.choice_first_result() # 选择结果列表中第一个
        try:
            self.get_baseinfo_item() # 主要信息
            print(self.item)
        except:
            self.save_fail('提取数据失败')

        self.save_html()  # 保存数据页面,后续可以提取完整信息
        # 先推数据,后推消息
        redis.hset(self.redis_key, 'data', self.item) # 向redis存数据
        self.send_msg_to_redis(msg='抓取成功', status='done')

        self.driver.quit() # 关闭浏览器
        # self.driver.service.process.pid # webdriver-server的pid


    def run(self):
        self._main()


if __name__ == '__main__':

    server = CrawlerServer()
    server.crawl()


3. 完成后的项目文件结构

在这里插入图片描述

4. 后续可以继续完善

  • 抓取更多的字段
  • 保存更多的数据页面
  • 以token命名,记录详细的日志信息
  • 对接第三方打码平台

小结

  1. 了解 crawler爬虫运行流程

    def run(self):
    self._main()

if name == ‘main’:

server = CrawlerServer()
server.crawl()

### 3. 完成后的项目文件结构

![在这里插入图片描述](https://img-blog.csdnimg.cn/09cc5e3b41464b719b2d411698c11c8e.png#pic_center)


### 4. 后续可以继续完善

- 抓取更多的字段
- 保存更多的数据页面
- 以token命名,记录详细的日志信息
- 对接第三方打码平台

_________________

## 小结
1. 了解 crawler爬虫运行流程
2. 了解 crawler爬虫模块实现

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/331280.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

电脑自带的录屏软件在哪?图文教学,教你如何快速录屏

很多小伙伴或许都听说过电脑有一款自带的录屏软件&#xff0c;但却不知道这款录屏软件在哪里。电脑自带的录屏软件在哪&#xff1f;其实很简单&#xff0c;如果你的电脑是Win10或者Win11的电脑&#xff0c;那么就可以使用电脑自带的录屏软件&#xff0c;一起跟着小编来看看吧。…

初次认识C++类

目录 前言&#xff1a; 面向过程和面向对象的区别&#xff1a; C语言&#xff1a; C&#xff1a; 类的引入&#xff1a; 类的定义&#xff1a; 类的权限&#xff1a; 类的作用域&#xff1a; 类的实例化&#xff1a; 类的大小计算&#xff1a; 空类或则只…

急速肝了一波ChatGPT,听说阿里面试题都没问题~

目录前言注册步骤&#xff1a;最后总结前言 互联网圈子里面ChatGPT现在实在是太火了&#xff0c;但是你还没用过&#xff1f;我只能说你OUT了&#xff0c;ChatGPT是什么呢&#xff1f; 由人工智能实验室OpenAI发布的对话式大型语言模型ChatGPT引爆中文互联网。它可以与人类轻松…

2022年ts学习记录

以下记录的是&#xff0c;我在学习中的一些学习笔记&#xff0c;这篇笔记是自己学习的学习大杂烩&#xff0c;主要用于记录&#xff0c;方便查找一、TS 是什么 &#xff1f;##1、简介TS&#xff1a;是TypeScript的简称&#xff0c;是一种由微软开发的自由和开源的编程语言。ts …

朗润外盘国际期货:SC原油市场情绪回暖领涨期市

今日值得回溯的三个行情&#xff1a;①SC原油主力合约今日收高4.23%&#xff0c;研报建议仍以震荡行情对待&#xff1f;②沪锡主力合约收涨3.20%&#xff0c;现在做多合适吗&#xff1f;③鸡蛋主力合约收跌1.32%&#xff0c;研报称这只是小幅回调。 【今日期市盘面概况】 整体…

深度学习 Day25——使用Pytorch实现彩色图片识别

深度学习 Day25——使用Pytorch实现彩色图片识别 文章目录深度学习 Day25——使用Pytorch实现彩色图片识别一、前言二、我的环境三、前期工作1、导入依赖项和设置GPU2、下载数据3、加载数据4、数据可视化四、构建CNN网络结构1、函数介绍2、构建CNN并打印模型3、可视化模型结构五…

Docker安装EalasticSearch、Kibana,安装Elasticvue插件

使用Docker快速安装部署ES和Kibana的前提&#xff1a;首先需要确保已经安装了Docker环境。 如果没有安装Docker的话&#xff0c;先在Linux上安装Docker。 有了Docker环境后&#xff0c;就可以使用Docker安装部署ES和Kibana了 一、安装ES 1、拉取EalasticSearch镜像 docker p…

SpringIOC之创建Bean的核心方法doGetBean

概述面向资源&#xff08;XML、Properties&#xff09;、面向注解定义的 Bean 是如何被解析成 BeanDefinition&#xff08;Bean 的“前身”&#xff09;&#xff0c;并保存至 BeanDefinitionRegistry 注册中心里面&#xff0c;实际也是通过 ConcurrentHashMap 进行保存。Spring…

强缓存与协商缓存

Ⅰ、http缓存 HTTP 缓存策略 分为 > 「强制缓存」 和 「协商缓存」 为什么需要 HTTP 缓存 呢 ? &#x1f447; 直接使用缓存速度 >> 远比重新请求快 缓存对象有那些呢 &#xff1f;&#x1f447; 「图片」 「JS文件」 「CSS文件」 等等 文章目录Ⅰ、http缓存Ⅱ…

Hi3559av100平台8路1080P/720P输入配置流程梳理

平台&#xff1a; hi3559av100 硬件连接&#xff1a; 8路YUV422 -> 4路 2lane MIPI -> hi3559av100 最终效果&#xff1a; 经过处理后&#xff0c;后端可以实现8路独立视频流处理&#xff1b; 可以自由和后级VPSS/VENC/VO等模块进行绑定和处理 前言&#xff1a; &…

Spring-整合Mybatis

Spring-整合Mybatis 6&#xff0c;Spring整合 6.1 Spring整合Mybatis思路分析 6.1.1 环境准备 在准备环境的过程中&#xff0c;我们也来回顾下Mybatis开发的相关内容: 步骤1:准备数据库表 Mybatis是来操作数据库表&#xff0c;所以先创建一个数据库及表 create database…

IDEA设置默认JDK、Maven版本问题汇总

遇到的问题&#xff1a;使用IDEA导入一个Maven多模块项目遇到关于JDK和Maven版本相关的问题&#xff0c;这里记录一下解决办法。如何修改当前项目JDK和Maven的版本&#xff1f;修改当前项目对应Maven版本&#xff1a;File -> Setting -> 搜索Maven修改JDK版本&#xff0c…

图像处理--OpenCV学习笔记

内容简介 OpenCV是应用广泛的开源图像处理库&#xff0c;包括基本的图像处理方法&#xff1a;几何变换&#xff0c;形态学变换&#xff0c;图像平滑&#xff0c;直方图操作&#xff0c;模板匹配&#xff0c;霍夫变换&#xff1b;特征提取和描述方法&#xff1a;理解角点特征&a…

Linux驱动开发基础__工作队列

目录 1 引入 2 内核函数 2.1 定义work 2.2 使用 work&#xff1a;schedule_work 2.3 其他函数 3 代码 3.1 gpio_key_drv.c 3.2 button_test.c 4 内部机制 4.1 Linux 2.x 的工作队列创建过程 4.2 Linux 4.x 的工作队列创建过程 1 引入 前面讲的定时器、下半部 task…

ASEMI整流模块MDQ100-16的优点是什么?

编辑-Z ASEMI整流模块MDQ100-16参数&#xff1a; 型号&#xff1a;MDQ100-16 最大重复峰值反向电压&#xff08;VRRM&#xff09;&#xff1a;1600V 最大RMS电桥输入电压&#xff08;VRMS&#xff09;&#xff1a;1700V 最大平均正向整流输出电流&#xff08;IF&#xff0…

【内网安全】——Linux提权姿势

作者名&#xff1a;白昼安全主页面链接&#xff1a; 主页传送门创作初心&#xff1a; 一切为了她座右铭&#xff1a; 不要让时代的悲哀成为你的悲哀专研方向&#xff1a; web安全&#xff0c;后渗透技术每日emo&#xff1a; 希望你在新的一年也能更好&#xff0c;不惧流言蜚语&…

Django drf使用Django自带的用户系统的注册功能实现

在写登录功能的时候看着网上的视频学着做,然后看了源码的时候发现了一些有意思的功能,因此写这一篇笔记分享给大家. 1.阅读Django自带用户系统源码 1.1 阅读User类源码 系统自带的用户系统的models文件的位置\site-packages\django\contrib\auth\models.py,打开这个文件发现…

python爬虫--re模块简介

Python re正则表达式模块用法详解 前面章节介绍了一些系统自带的工具函数&#xff0c;如 id()、max()。这些函数在 Python 启动时会自动加载进来&#xff0c;不需要我们做任何事情。但并不是所有的模块都是自动加载进来的&#xff0c;因为有些模块不常用&#xff0c;它们只是在…

记录一次web server服务器编程过程中的bug

按照书上和视频中的代码比对没有问题&#xff0c;但是read函数输出不了连接的http请求&#xff0c;不断编译了好几遍还是不行&#xff0c;确定是端口的问题 首先&#xff0c;在云服务器中安全规则里已经允许了相应端口&#xff0c;如果还不可以&#xff0c;可以查看一下系统防火…

MyBatis 入门教程详解

✅作者简介&#xff1a;2022年博客新星 第八。热爱国学的Java后端开发者&#xff0c;修心和技术同步精进。 &#x1f34e;个人主页&#xff1a;Java Fans的博客 &#x1f34a;个人信条&#xff1a;不迁怒&#xff0c;不贰过。小知识&#xff0c;大智慧。 &#x1f49e;当前专栏…