【爬虫实战】国家企业公示网-crawler爬虫抓取数据
crawler爬虫实现
- 1. crawler功能
- 2. crawler代码实现
- 3. 完成后的项目文件结构
- 4. 后续可以继续完善
学习目标
- 了解 crawler爬虫运行流程
- 了解 crawler爬虫模块实现
1. crawler功能
- 初始化driver
- 输入公司名称,并点击
- 判断是否需要验证
- 如果需要验证,获取验证图片并保存
- 获取打码坐标
- 点击验证图片
- 判断查询结果
- 选择第一条查询结果
- 获取主要信息
- 保存数据页面
- 向redis中发送信息
- 对失败情况进行保存,关闭driver,推送失败信息
- 组织抓取逻辑,成功关闭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命名,记录详细的日志信息
- 对接第三方打码平台
小结
- 了解 crawler爬虫运行流程
- 了解 crawler爬虫模块实现
【爬虫实战】国家企业公示网-crawler爬虫抓取数据相关推荐
- 【爬虫实战】国家企业公示网-项目分析
国家企业公示网项目分析 前言 1. 确定抓取流程,确定数据位置 1.1 网站首页 1.2 行为验证图片 1.3 选择列表页中第一个公司 1.4 确定数据位置 1.5 保存数据页面 2. 项目代码组件 ...
- 【爬虫实战】国家企业公示网-运行效果
国家企业公示网-运行效果 0. 首页--接口/说明文档 1. 启动爬虫 2. 访问手动打码页面 3. 打码后返回页 4. 查询结果 4.1 抓取中 4.2 抓取成功 4.3 抓取失败 5. 静态文件夹 ...
- 国家企业公示网项目分析
国家企业公示网项目分析 学习目标: 了解 获取数据的抓取流程 了解 每个组件的功能 了解 项目运行流程 演示运行效果,确定被抓取网站状态正常 1. 确定抓取流程,确定数据位置 使用selenium控制 ...
- Python爬虫入门教程 18-100 煎蛋网XXOO图片抓取
写在前面 很高兴我这系列的文章写道第18篇了,今天写一个爬虫爱好者特别喜欢的网站煎蛋网http://jandan.net/ooxx,这个网站其实还是有点意思的,网站很多人写了N多的教程了,各种方式的都 ...
- Python爬虫入门教程 18-100 煎x网XO图片抓取
1. 煎x网XO-写在前面 本文涉及的网址请查看评论区 很高兴我这系列的文章写道第18篇了,今天写一个爬虫爱好者特别喜欢的网站煎x网 敏感站点 ,这个网站其实还是有点意思的,网站很多人写了N多的教程了 ...
- big sur 关闭sip_青岛市第二批拟关闭退出化工生产企业公示
日前,青岛公示青岛市第二批拟关闭退出化工生产企业.拟关闭退出企业共82家.详情如下: 青岛市第二批拟关闭退出化工生产企业公示 根据<关于确认第二批关闭退出化工生产企业的通知>(鲁化安转函[ ...
- java爬虫实战(3):网易云音乐评论,歌曲,歌单,歌词下载
java爬虫实战(3):网易云音乐评论,歌曲,歌单,歌词下载 *本实战仅作为学习和技术交流使用,转载请注明出处: *此文章很早便在草稿箱中,由于编写时事情较多,临时中断,现暂时发表,后续补上(2019 ...
- 2018中国地理信息产业百强企业公示名单
2018中国地理信息产业百强企业公示名单 1 北京四维图新科技股份有限公司 2 北京北斗星通导航技术股份有限公司 3 北京高德云图科技有限公司 4 正元地理信息有限责任公司 5 ...
- 爬虫实战(二)—利用requests、selenium爬取王者官网、王者营地APP数据及pymongo详解
概述 可关注微信订阅号 loak 查看实际效果. 代码已托管github,地址为:https://github.com/luozhengszj/LOLGokSpider ,包括了项目的所有代码. 本文 ...
最新文章
- 用枚举来处理java自定义异常
- UA MATH564 概率论VI 数理统计基础5 F分布
- 【风控建模】互联网金融-机器学习及评分卡构建
- 2018明星学术公众号TOP 10重磅发布,PaperWeekly再度入选
- 对象和json相互转换
- Java 如何设置时间_如何在Java中设置尊重用户操作系统设置的日期和时间格式
- mysql not null 索引_MySQL中 IS NULL、IS NOT NULL、!= 能用上索引吗?
- 强悍的 Ubuntu —— 粘贴板
- 遍历frame中的表单:
- jquery实现上拉加载更多
- FFT(不ji是干甚用的diao操作)
- 萤火虫算法 java_AGSO 萤火虫算法
- 数据结构(C#)_排序算法(冒泡排序)
- ajax到底怎么读呢
- 消防工程师培训十大名师介绍
- [YOLOv7/YOLOv5系列算法改进NO.20]Involution新神经网络算子引入网络
- Xdelta3 bsdiff Courgette三种差分算法比较
- 废水硝酸盐的深度去除
- Weir:原生 TiDB 支持的数据库中间件
- 野火STM32学习(6)
热门文章
- Dorado7 notify非alert 输入框prompt confirm layer dialoger,layer.msg,toast效果,几秒关闭layer.load layer.open
- brpc源码解析(四)—— Bthread机制
- 单片机 c语言 可控硅,单片机控制可控硅电路
- CSS3知识点复习与总结
- linux查看web密码,fuel7.0 openstack webui 默认密码查看
- 安装YApi 接口管理平台
- mysql人像伴随模型_还不了解MySQL跨行事务模型吗?看完这篇你也就差不多了!...
- 【ubuntu】解决 Certificate verification failed: The certificate is NOT trusted
- 微博蓝V认证怎么做?(微博企业认证)
- API中endpoints是什么意思?