Loading... [药监局](https://www.nmpa.gov.cn/datasearch/home-index.html)可以查询每个药企的许可证编号。当我们如果想查找多家药企的许可证编号时候,就必须一条条查询,很麻烦。所以就需要想办法使用自动化程序来帮我们处理。后面当自动化程序运行起来后,发现这个网站是有防D(DDOS防御)的。而且频率限制的相当低。通常这种有两个解决办法。 - 拉长每条数据爬取间隔,这样显然会让爬取数据的效率大大降低。 - 通过动态代理IP来实现绕过防D检测,这样效率会非常高,效率无上限。但是要找到好用干净的IP还是比较难的。我在接下来测试时候,还交了40元学费后发现这些地址全部被药监局封了。白瞎了这40块钱。 **声明:本文章中所有内容仅供学习交流,抓包内容、敏感网址、数据接口均已做脱敏处理,严禁用于商业用途和非法用途,否则由此产生的一切后果均与作者无关,若有侵权,请联系我立即删除!** # 什么是爬虫 接触到现在,也许很多人眼里,爬虫就是获取网页数据的。自然也把实现这一目标的都归为爬虫。这么理解也没问题。那实现这一目的的有哪些方式呢? - 通过request请求直接发送请求获取response响应结果。特点效率高,难度高。(单子价格高,通常一个网页的爬虫通过此方式做出来,价格在几百到上万不等) - 自动化测试工具selenium、drissionpage 实现自动对浏览器的操作。特点效率慢,难度低。 按我理解request才是真正的爬虫(直接交互数据),而后者selenium、drissionpage只是自动化测试工具。 # 网站分析 ## 查看请求包 打开浏览器后点击F12调试,再刷新下网页就能够看到所有的http请求。 ![](https://storage.bbcking5.com/文章内部图/自动/image-20240722210642597.png) 如上图,刚调试就遇到了两个问题: - 无限被Debug,正常刚打开调试不会进入Debug。后来关闭Debug,点击左侧三角箭头继续运行才加载出来查询结果。 - 发生卡死现象,当想把一个个改成跳过Debug,可能触发了什么逻辑,直接网页卡死。后来也是按如上操作才出现了下面的请求结构。接下去我们来分析一下。 ![image-20240722212024940](https://storage.bbcking5.com/文章内部图/自动/image-20240722212024940.png) 网页上其实一个选择药企类型,一个是输入药企名称。然后点击查询按钮返回结果。正常的开发逻辑比较像上图的请求。同时我们看到药企的类型和名称全部是加密的,在header中还有sign签名。需要分析三块地方的的加密方法甚至更多。再加上无限Debug。对我爬虫初学者来说的确有点棘手,所以对于这种效率要求不高的情况下,使用自动化测试工具是个不错的选择。 # 自动化测试工具 当我使用selenium来访问药监局网站时候发现无法打开。为什么手动可以打开,用selenium无法打开?在我们手动直接访问、selenium、drissionpage访问有什么区别呢?访问https://bot.sannysoft.com/测试看下。 ## 手动直接访问 浏览器特征全部通过。 ![image-20240722230826556](https://storage.bbcking5.com/文章内部图/自动/image-20240722230826556.png) ## selenium 会被检测到webdriver属性。测试药监局网站时候,由于被检测到webdriver属性,则直接显示白屏。 ```python from selenium import webdriver import time driver = webdriver.Chrome() driver.get("https://bot.sannysoft.com/") time.sleep(60) driver.quit() ``` ![image-20240722231002471](https://storage.bbcking5.com/文章内部图/自动/image-20240722231002471.png) ## drissionpage 和手动访问完全一致 ,浏览器特征全部通过。 ```python import time from DrissionPage import ChromiumPage page = ChromiumPage() page.get("https://bot.sannysoft.com/") time.sleep(60) page.quit() ``` ![image-20240722231214186](https://storage.bbcking5.com/文章内部图/自动/image-20240722231214186.png) # 编写代码 自动化测试工具本质还是模拟浏览器中点击输入等等操作。最常见的无非就是点击、输入、新建和切换标签页。想具体了解可以看[drissonpage官方文档](https://www.drissionpage.cn/ChromiumPage/tab)。下面就以官网文档来演示,如何把这些操作,通过代码来实现。 ## 准备工作 - 安装Python V3.10以上版本 - pip安装软件包 `pip install drissionpage` - 安装chrome浏览器 ## 打开网页 将上面的`page.get("https://bot.sannysoft.com/")` 网址替换为https://www.drissionpage.cn/ChromiumPage/tab 此代码运行后就是打开网页的操作。 ## 定位元素 通常元素定位有三个方式,一种是通过html元素选择器来定位,第二种种则是通过元素标签路径来定位,第三种元素标签来定位。 ![image-20240728213202272](https://storage.bbcking5.com/文章内部图/自动/image-20240728213202272.png) 如上图,我们要定位到右上角搜索框,那就在搜索框位置右键-->检查。弹出调试控制台,直接会到达该元素所在位置。其元素class选择器名称为DocSearch-Button-Placeholder,且搜索后该页面只有一个。(id选择器全局唯一,class选择器是有可能名称重复的,所以需要搜索确认下是否只有一个)。而sapn、div、button位于每行开头处的是标签。 ### 元素选择器 ```python page.ele('@class=DocSearch-Button-Placeholder').click() #.click 是触发点击动作,.click之前则是定位元素。 ``` ### 元素标签路径 ![image-20240729195229635](https://storage.bbcking5.com/文章内部图/自动/image-20240729195229635.png) ```python page.ele( '/html/body/div[1]/div/div/header/form/input').input('标签页操作') #搜索「标签页操作」相关内容 page.ele('#docsearch-item-1').click() #和css语法类似,使用id选择器时候需要用#开头。点击第一个搜索到的内容 ``` ### 元素标签 通过标签来定位元素用的不多,因为标签名称重复的太多了。默认都是最靠上的。通过标签定位的话需要层层递进。 ```python # 此处代码和当前网页无关 tb_ele = page.ele('tag:tbody') #定位到表单元素 tr_eles = tb_ele.eles('tag:tr') #定位到所有行元素、ele是定位到一个元素。定位多个元素为列表时候使用eles for tr in tr_eles: #循环对其中元素操作 name = tr.ele('@class=el-table_1_column_2 is-center ').text ``` ## 输入内容查找 `page.ele( '/html/body/div[1]/div/div/header/form/input').input('标签页操作') ` 语法基本都一样前面是定位,后面是动作。input就是输入内容的动作。 ## 获取文本 ```python name = page.ele('@class=DocSearch-Hit-source').text #text是获取文字元素的内容,并赋值给name变量 print(name) #打印文字内容 ``` # 使用代理 ## drissionpage包自带 drissionpage 包直接支持的代理缺点就是只能在创建ChromiumPage实例之前设置。当开始自动执行后无法切换代理。代码如下。 ```python from DrissionPage import ChromiumPage,DriverOptions options = DriverOptions(read_file=False) proxy= '112.250.249.60:38072' options.set_proxy(proxy) page = ChromiumPage(addr_driver_opts=options) page.get('https://www.baidu.com/') print(page.html) ``` ## 浏览器插件 通过proxy_switchyomega浏览器插件实现代理。怎么说呢,被防D了,降低点速度好了,如果说通过代理来切换ip,来破DDOS防御,达到提速效果的话,我感觉是多虑了。首先通过每个ip爬取5条数据后换一个ip来访问,这样做的确能提高一定爬取数据速度没错。但这毕竟是自动化测试工具,就算这么做最多也就提升个五六倍吧,或者再加10个浏览器实例一起跑。那速度也就从原来7秒一条数据,上升到1秒10条数据。这种方式吃自己电脑资源不说,速度提升上也差强人意。通过request直接构造请求数据包,再通过设置代理,直接发后端,一秒钟少说也能达到几百上千。不过也放一下代码吧。 ```python import platform from DrissionPage import ChromiumPage, ChromiumOptions from loguru import logger import requests headers = { 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7', 'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8,en-US;q=0.7,en-GB;q=0.6', 'Cache-Control': 'no-cache', 'Connection': 'keep-alive', 'DNT': '1', 'Pragma': 'no-cache', 'Upgrade-Insecure-Requests': '1', 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36', } params = { 'count': '5', 'neek': '112095', 'type': '1', 'sep': '4', 'sb': '', 'ip_si': '1', 'mr': '0', } response = requests.get('http://tiqu.pyhttp.taolop.com/getflowip', params=params, headers=headers, verify=False) def get_free_ip(): url = "https://www.zdaye.com/free/?ip=&adr=&checktime=&sleep=&cunhuo=&dengji=1&nadr=&https=&yys=&post=&px=" browser.get(url, retry=3, interval=1, timeout=15) ip_ports = [] for tr in browser.eles('x://table[@id="ipc"]//tr')[1:]: tds = [td.text for td in tr.eles("x://td")] ip_ports.append((f"{tds[0]}:{tds[1]}", tds[3])) print(len(ip_ports), ip_ports) return ip_ports def switch_ip(ip_port=None): global set_proxy if ip_port: # 设置proxy ip, port = ip_port.split(":") tab = browser.new_tab() tab.get( "chrome-extension://padekgcemlokbadohgkifijomclgjgif/options.html#!/profile/proxy") tab.ele( 'x://input[@ng-model="proxyEditors[scheme].host"]').input(ip, clear=True) tab.ele( 'x://input[@ng-model="proxyEditors[scheme].port"]').input(port, clear=True) tab.ele('x://a[@ng-click="applyOptions()"]').click() tab.wait(1) # 提示框 txt = tab.handle_alert() print("提示框", txt) tab.handle_alert(accept=False) if not omega_proxy: # 切换proxy tab.get( "chrome-extension://padekgcemlokbadohgkifijomclgjgif/popup/index.html#") tab.wait(1) tab.ele('x://span[text()="proxy"]').click() set_proxy = True else: tab = browser.new_tab() tab.get("chrome-extension://padekgcemlokbadohgkifijomclgjgif/popup/index.html#") tab.ele('x://span[text()="[直接连接]"]').click() if len(browser.tab_ids) > 1: print("当前tab个数", len(browser.tab_ids)) tab.close() if platform.system().lower() == 'windows': # .set_paths(browser_path=r"C:\Program Files (x86)\Microsoft\Edge\Application\msedge.exe") co = ChromiumOptions() else: co = ChromiumOptions().set_paths(browser_path=r"/opt/google/chrome/google-chrome") co.headless(True) # 设置无头加载 无头模式是一种在浏览器没有界面的情况下运行的模式,它可以提高浏览器的性能和加载速 # co.incognito(True) # 无痕隐身模式打开的话,不会记住你的网站账号密码的 # 禁用沙箱 禁用沙箱可以避免浏览器在加载页面时进行安全检查,从而提高加载速度 默认情况下,所有Chrome 用户都启用了隐私沙盒选项 https://zhuanlan.zhihu.com/p/475639754 co.set_argument('--no-sandbox') co.set_argument("--disable-gpu") # 禁用GPU加速可以避免浏览器在加载页面时使用过多的计算资源,从而提高加载速度 co.set_user_agent( user_agent='Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36') # 设置ua co.set_timeouts(6, 6, 6) co.set_local_port(9211) # 1、设置switchyOmega插件 co.add_extension(r'C:\Users\Ad\Desktop\proxy_switchyomega-2.5.20') browser = ChromiumPage(co) # 2、重置switchyOmega插件 omega_proxy = False switch_ip() browser.get("https://www.ip138.com/", retry=0) html_text = browser.get_frame( 'x://div[@class="hd"]//iframe').ele('text:您的iP地址是').text logger.success(f">>>>当前的ip {html_text}") # 3、随机切换代理ip # ip_all = get_free_ip() ip_all = [{"ip": "10.1.3.56", "port": 7890, "expire_time": "2024-04-27 22:24:00"}] for ips in ip_all: logger.info(f"~~~切换ip,now {ips['ip']}") # 重置switchyOmega插件 switch_ip(f"{ips['ip']}:{ips['port']}") browser.wait(1) try: browser.get("https://www.baidu.com/", retry=0) browser.get("https://www.ip138.com/", retry=0) browser.get("https://www.google.com/", retry=0) html_text = browser.get_frame( 'x://div[@class="hd"]//iframe').ele('text:您的iP地址是').text logger.success(f">>>>>>>>切换代理成功 {html_text}") except Exception as err: logger.error(f"----------切换代理失败 dp {err}") browser.wait(10) browser.quit() ``` Last modification:July 29, 2024 © Allow specification reprint Like 如果觉得我的文章对你有用,请随意赞赏