crawler爬虫实现

  • 1. crawler功能
  • 2. crawler代码实现
  • 3. 完成后的项目文件结构
  • 4. 后续可以继续完善

学习目标

  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) # tokenself.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 = 0while i < 3:try: # 手动显式等待验证码图片出现time.sleep(1)# 获取图片对象self.captcha_img = self.driver.find_element_by_xpath('//img[@class="geetest_item_img"]')return # self.captcha_img != Noneexcept:# 存在不需要验证的情况; 也存在滑动的情况# 对于滑动拼图就passif self.driver.current_url != self.url:return # self.captcha_img = Nonei += 1def 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.contentprint(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.sizeim.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 = 0while i < 180:captcha_offset = redis.hget(self.redis_key, 'captcha_params')if captcha_offset is not None:return captcha_offsettime.sleep(1)i += 1return None # 超时,没有获取打码坐标# 调用第三方打码elif self.crack_captcha_mode == '1':"""暂不实现"""return Noneelse: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 Truedef 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 = 0while i<5: # 手动显式等待,等待页面加载完毕,以reg_no是否出现为标志reg_no = self.driver.find_elements_by_xpath('//*[@id="REG_NO"]')if reg_no != []:breaktime.sleep(3)i += 1# 统一社会信用代码/注册号REG_NOreg_no = self.driver.find_elements_by_xpath('//*[@id="REG_NO"]')if reg_no == []:return Falseself.item['reg_no'] = reg_no[0].text if id != [] else ''# 企业名称CORP_NAMEcorp_name = self.driver.find_elements_by_xpath('//*[@id="CORP_NAME"]')self.item['corp_name'] = corp_name[0].text if id != [] else ''# 类型ZJ_ECON_KINDzj_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_NAMEoper_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_CAPIreg_cpi = self.driver.find_elements_by_xpath('//*[@id="REG_CAPI"]')self.item['reg_cpi'] = reg_cpi[0].text if id != [] else ''# 成立日期START_DATEstart_date = self.driver.find_elements_by_xpath('//*[@id="START_DATE"]')self.item['oper_man_name'] = start_date[0].text if id != [] else ''# 营业期限自FARE_TERM_STARTfare_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_ENDfare_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_ORGbelong_org = self.driver.find_elements_by_xpath('//*[@id="BELONG_ORG"]')self.item['belong_org'] = belong_org[0].text if id != [] else ''# 核准日期CHECK_DATEcheck_date = self.driver.find_elements_by_xpath('//*[@id="CHECK_DATE"]')self.item['check_date'] = check_date[0].text if id != [] else ''# 登记状态CORP_STATUScorp_status = self.driver.find_elements_by_xpath('//*[@id="CORP_STATUS"]')self.item['corp_status'] = corp_status[0].text if id != [] else ''# 住所ADDRaddr = self.driver.find_elements_by_xpath('//*[@id="ADDR"]')self.item['addr'] = addr[0].text if id != [] else ''# 经营范围FARE_SCOPEfare_scope = self.driver.find_elements_by_xpath('//*[@id="FARE_SCOPE"]')self.item['fare_scope'] = fare_scope[0].text if id != [] else ''return Truedef 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('没有公司名称,查个毛线')returnif self.token is None:print('想单文件抓取自己写啊!')returntry: # 初始化driverself.init_driver()except:self.save_fail('初始化失败')returnself.send_msg_to_redis(msg='抓取进行中', status='crawling')try: # 输入公司名称点击self.send_company_name()except:input(11)self.save_fail('输入公司名称点击失败')returnself.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')returnif 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的piddef run(self):self._main()if __name__ == '__main__':server = CrawlerServer()server.crawl()

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

4. 后续可以继续完善

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

小结

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

【爬虫实战】国家企业公示网-crawler爬虫抓取数据相关推荐

  1. 【爬虫实战】国家企业公示网-项目分析

    国家企业公示网项目分析 前言 1. 确定抓取流程,确定数据位置 1.1 网站首页 1.2 行为验证图片 1.3 选择列表页中第一个公司 1.4 确定数据位置 1.5 保存数据页面 2. 项目代码组件 ...

  2. 【爬虫实战】国家企业公示网-运行效果

    国家企业公示网-运行效果 0. 首页--接口/说明文档 1. 启动爬虫 2. 访问手动打码页面 3. 打码后返回页 4. 查询结果 4.1 抓取中 4.2 抓取成功 4.3 抓取失败 5. 静态文件夹 ...

  3. 国家企业公示网项目分析

    国家企业公示网项目分析 学习目标: 了解 获取数据的抓取流程 了解 每个组件的功能 了解 项目运行流程 演示运行效果,确定被抓取网站状态正常 1. 确定抓取流程,确定数据位置 使用selenium控制 ...

  4. Python爬虫入门教程 18-100 煎蛋网XXOO图片抓取

    写在前面 很高兴我这系列的文章写道第18篇了,今天写一个爬虫爱好者特别喜欢的网站煎蛋网http://jandan.net/ooxx,这个网站其实还是有点意思的,网站很多人写了N多的教程了,各种方式的都 ...

  5. Python爬虫入门教程 18-100 煎x网XO图片抓取

    1. 煎x网XO-写在前面 本文涉及的网址请查看评论区 很高兴我这系列的文章写道第18篇了,今天写一个爬虫爱好者特别喜欢的网站煎x网 敏感站点 ,这个网站其实还是有点意思的,网站很多人写了N多的教程了 ...

  6. big sur 关闭sip_青岛市第二批拟关闭退出化工生产企业公示

    日前,青岛公示青岛市第二批拟关闭退出化工生产企业.拟关闭退出企业共82家.详情如下: 青岛市第二批拟关闭退出化工生产企业公示 根据<关于确认第二批关闭退出化工生产企业的通知>(鲁化安转函[ ...

  7. java爬虫实战(3):网易云音乐评论,歌曲,歌单,歌词下载

    java爬虫实战(3):网易云音乐评论,歌曲,歌单,歌词下载 *本实战仅作为学习和技术交流使用,转载请注明出处: *此文章很早便在草稿箱中,由于编写时事情较多,临时中断,现暂时发表,后续补上(2019 ...

  8. 2018中国地理信息产业百强企业公示名单

    2018中国地理信息产业百强企业公示名单 1    北京四维图新科技股份有限公司 2    北京北斗星通导航技术股份有限公司 3    北京高德云图科技有限公司 4    正元地理信息有限责任公司 5 ...

  9. 爬虫实战(二)—利用requests、selenium爬取王者官网、王者营地APP数据及pymongo详解

    概述 可关注微信订阅号 loak 查看实际效果. 代码已托管github,地址为:https://github.com/luozhengszj/LOLGokSpider ,包括了项目的所有代码. 本文 ...

最新文章

  1. 用枚举来处理java自定义异常
  2. UA MATH564 概率论VI 数理统计基础5 F分布
  3. 【风控建模】互联网金融-机器学习及评分卡构建
  4. 2018明星学术公众号TOP 10重磅发布,PaperWeekly再度入选
  5. 对象和json相互转换
  6. Java 如何设置时间_如何在Java中设置尊重用户操作系统设置的日期和时间格式
  7. mysql not null 索引_MySQL中 IS NULL、IS NOT NULL、!= 能用上索引吗?
  8. 强悍的 Ubuntu —— 粘贴板
  9. 遍历frame中的表单:
  10. jquery实现上拉加载更多
  11. FFT(不ji是干甚用的diao操作)
  12. 萤火虫算法 java_AGSO 萤火虫算法
  13. 数据结构(C#)_排序算法(冒泡排序)
  14. ajax到底怎么读呢
  15. 消防工程师培训十大名师介绍
  16. [YOLOv7/YOLOv5系列算法改进NO.20]Involution新神经网络算子引入网络
  17. Xdelta3 bsdiff Courgette三种差分算法比较
  18. 废水硝酸盐的深度去除
  19. Weir:原生 TiDB 支持的数据库中间件
  20. 野火STM32学习(6)

热门文章

  1. Dorado7 notify非alert 输入框prompt confirm layer dialoger,layer.msg,toast效果,几秒关闭layer.load layer.open
  2. brpc源码解析(四)—— Bthread机制
  3. 单片机 c语言 可控硅,单片机控制可控硅电路
  4. CSS3知识点复习与总结
  5. linux查看web密码,fuel7.0 openstack webui 默认密码查看
  6. 安装YApi 接口管理平台
  7. mysql人像伴随模型_还不了解MySQL跨行事务模型吗?看完这篇你也就差不多了!...
  8. 【ubuntu】解决 Certificate verification failed: The certificate is NOT trusted
  9. 微博蓝V认证怎么做?(微博企业认证)
  10. API中endpoints是什么意思?