通用性基本爬虫问题的解决思路

2022-04-26 19:04:08 浏览数 (1)

前言:

对于爬虫和反爬一直是矛盾的存在的,今天就一个粉丝提出的爬虫问题梳理一下我对爬虫类问题的解决。

需求分析:

爬取目标网站:中国商标网

爬取目标网址:

http://wsgg.sbj.cnipa.gov.cn:9080/tmann/annInfoView/annSearch.html?annNum=1739

爬取目标内容:商标网1739期的商标图片

浏览器加载完成页面后,在点击公告的【查看】,会弹出一个用easyui写的弹窗。内容如下:

右边栏目是由一张张的图片组成。在当前页面右键其实可以查看到图片的链接:

图片的链接复制出来:

http://sbggwj.sbj.cnipa.gov.cn:8000/tmann/group2/M00/3D/90/yBQCIGB0BHiAYbL-AAIGseLOTN8597.jpg

是一个jpg的网络地址,把这url在浏览器页面打开就是当前商标的图片。在页面的左下角提供了下载功能。也是可以将内容下载下来,但是当前一共有58566条,如果我们仅仅现在10条8条的,可以通过下载按钮下来。如果下载1000张图,甚至1万张。那么肯定不能使用手动下载的方式。下面自动化的爬虫该上场了。

尝试解决:

首先,先考虑使用requests包。因为我们不清楚是不是该网上有什么样的反爬机制。所以就默认没有反爬,用最简单的requests的请求来处理。

打开浏览器的F12开发者控制台。选择【网络】清空内容,然后点击一个查看,弹框的请求都会在这里出现。我们一个个的选择查看,发现第四个是一个我们想要的找的请求。

在它的响应中是图片的链接。

然后我们分析一下这个请求,请求链接:

http://wsgg.sbj.cnipa.gov.cn:9080/tmann/annInfoView/imageView.html?O56fzBVE=5DwQxtsG2CPY_BgOXmR4Vb9OFHGwmvoE680VuHMVJXCJd2fc0iOdVNS6fvTjZdLp1qmOSYdFxMqb_1IfgSt9kEakKEnbdUp4CXXij1CLoe2UngRjFI26ndsCHecEQ8dshLTLWuoEFR6PbII8HehjoDyevKvSRRqF6RwYqpEI_t4AbOfd2MM_fLCFfWRL3J5zbqxQ698AB.e2OWORXLCqDixjbtB_gMH1s7ZDBfottQCKKtzg8b62OCKXds0We5gNVKmZmJnvJ9iRcgMUpA25k3WMmcpeU_yBSr1vOgkfIAjkZ6ARE_o7Wi7f0YGis0WGGACf9QuV9V.jqAC1DEyEJqfrLeDz11Q_.Ib1HMZy7PJZz69sMCCcwPri6mpHoBXUk

请求方式:POST

请求数据:

id=e48b920078abe67e0178af97dddb24bd

pageNum=1

flag=1

像这种的URL后面带一个参数的 ,如本例子中的,O56fzBVE=XXXX....,这种一般就是加密字符串,这种加密字符串通常是一次性消费的,而且并不一定是对称加密的。所以url中有这些乱七八糟的,是一种不祥的预感。

但是还是要尝试一下,先在PyCharm中使用requests试验一下能否请求成功,如果能直接获得这个JSON包的数据,那么就可以直接下载了,这就是最简单的爬虫方式。而且从POST的请求内容来看pageNum 就是分页的参数。

代码语言:javascript复制
import requests
from copyheaders import headers_raw_to_dict

url = "http://wsgg.sbj.cnipa.gov.cn:9080/tmann/annInfoView/imageView.html?O56fzBVE=5DwQxtsG2CPY_BgOXmR4Vb9OFHGwmvoE680VuHMVJXCJd2fc0iOdVNS6fvTjZdLp1qmOSYdFxMqb_1IfgSt9kEakKEnbdUp4CXXij1CLoe2UngRjFI26ndsCHecEQ8dshLTLWuoEFR6PbII8HehjoDyevKvSRRqF6RwYqpEI_t4AbOfd2MM_fLCFfWRL3J5zbqxQ698AB.e2OWORXLCqDixjbtB_gMH1s7ZDBfottQCKKtzg8b62OCKXds0We5gNVKmZmJnvJ9iRcgMUpA25k3WMmcpeU_yBSr1vOgkfIAjkZ6ARE_o7Wi7f0YGis0WGGACf9QuV9V.jqAC1DEyEJqfrLeDz11Q_.Ib1HMZy7PJZz69sMCCcwPri6mpHoBXUk"
#为了方便,直接把请求头全部复制下来
headers = b'''
Host: wsgg.sbj.cnipa.gov.cn:9080
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:89.0) Gecko/20100101 Firefox/89.0
Accept: application/json, text/javascript, */*; q=0.01
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
X-Requested-With: XMLHttpRequest
Content-Length: 52
Origin: http://wsgg.sbj.cnipa.gov.cn:9080
Connection: keep-alive
Referer: http://wsgg.sbj.cnipa.gov.cn:9080/tmann/annInfoView/annSearch.html?annNum=1739
Cookie: UM_distinctid=17a1792522562-0f4665b34dafe1-4c3f2d73-144000-17a179252264ac; tmas_cookie=2272.7689.15400.0000; goN9uW4i0iKzS=5TaLrZn9nLTvjnt__ZYhEfxdJnTtsB4BQu137RATan.uvVZtP1EvB7fC7R2jC_mzf6bZQKMgZbYMHtp1MZVnEtG; goN9uW4i0iKzT=53WaTXKkWJNGqqqm_oMiGDaYZxuBJDYrDiRfQkLG4oBqRFu6KLoaHbGKm_DCJvhYtHWkj0A4u7bLG1whU3K64UM_YDc1F4wr4zLFEghy6fiaWqRXY.hbqh4auJ1NKylVtmJgQ6DEY67085pPwy2rpxcAsFIHby_aWyPj1E7DW9IRLItD1VhrvyQVywmJZVGxQN1TGDEBSEWvnNebsp_4BiJ2hO.DrXlTPi3cTMsyXhyLz_hiC8h3pUUQRCteNaqFf27Kk_mQSk74CWL7B3wh_pnFCQpAMT5Xj4w4hMISxk9O5EYfrhqN6Z.2NTuNHH_R3tU.en2CdS3kGFmsAX5xZX5; 018f9ebcc3834ce269=e30ae012ff8dc9ccbaa9c7e48f97e030; JSESSIONID=00005MT537Hx0HRr2Eg5ojwO7SD:1bm104qqp
Pragma: no-cache
Cache-Control: no-cache
'''
data = {'id': 'e48b920078abe67e0178af97dddb24bd',
        'pageNum': 1,
        'flag': 1}
#requests请求
response = requests.post(url=url, headers=headers_raw_to_dict(headers),
                         data=data)
#设置编码
response.encoding = 'utf-8'
print(response.status_code)
print(response.text)

结果是这样的,被服务器拦截了,而且给了提示:

【该操作已触发系统访问防护规则】

代码语言:javascript复制
400
<html>
<head>
 <meta charset="UTF-8">
<script>
function randomNum(len) {
  len = len || 32;
  var $chars = '0123456789';    
  var maxPos = $chars.length;
  var str = '';
  for (i = 0; i < len; i  ) {
    str  = $chars.charAt(Math.floor(Math.random() * maxPos));
  }
  return str;
}
</script>
<style>
/*系统提示*/
.systembd {  min-height:500px;}
.systempic { width:500px; margin:100px auto;}
.systempic h1 { font-size:30px; color:#333; text-align:center; margin:15px auto;}
.systempic p { font-size:18px; color:#666666; text-align:center;margin:15px auto;}
</style>
</head>

  <div class="systembd">
    <div class="systempic slsbd">
      <h1>出错啦!</h1>
      <p>该操作已触发系统访问防护规则,请于1-3小时后重试。</p>
      <p>并请排除不良因素,如:</p>
      <p>一、所用终端已安装第三方查询软件或插件</p>
      <p>二、共用公网IP地址触发系统访问防护规则</p>
      <p>事件ID:<span id="eventid"></span></p>
      <p><a href="/">返回首页</a></p>
    </div>
  </div>
  <script>
  var str=randomNum(4) '-' randomNum(4) '-' randomNum(4);
     document.getElementById("eventid").innerHTML=str;
   var img = new Image();
   img.src="/" str ".png";
</script>
</html>

Process finished with exit code 0

现在我们去页面中寻找一下这个请求的发出源,具体的内容在页面的一个js文件中:

代码语言:javascript复制
<script type="text/javascript" src="js/business/annSearch/annSearch.js"></script>

其中有如下的片段:当前这个请求是通过ajax来完成的,我们看源代码中并没有真正的链接中携带的 O56fzBVE=5DwQxtsG2CPY_....的一大串内容。

在看浏览器的开发者工具中:

每一个ajax的链接的携带的参数是不一样的,说明在前端进行了参数的加密,而且也确定了之前我们的猜想,这一串字符串就是用来加密验证的。

解决这种问题的思路就是两个:

1.找到加密方式,再尝试进行。

2.抛弃使用requests,改用selenium

方式1:需要找到解密方式很难,失败率很高,但是如果能找到,那么爬取速度会很快。

方法2:selenium是一个自动化测试工具,也应用于爬虫中,可以解决动态加密的问题,但是速度相对requests是慢的。

切换思路:

改用selenium,使用selenium需要先下载chrome的驱动

下载地址:

http://chromedriver.storage.googleapis.com/index.html

  1. 先查看自己本机的浏览器版本
  2. 到上面地址下载对应的驱动,大版本一致即可

我的电脑是91版本的64位Windows,没有64,下载32的也行。

下载完成解压后,将chromedriver.exe放置到python的目录下,然后按照解决思路开始编写代码。

版本1代码:

代码语言:javascript复制
from selenium.webdriver import Chrome

# 使用selenium来进行测试
def getChrome():
    #获取谷歌浏览器的启动
    driver = Chrome()
    #打开链接
    driver.get("http://wsgg.sbj.cnipa.gov.cn:9080/tmann/annInfoView/annSearch.html?annNum=1739")
    driver.implicitly_wait(20)
    driver.find_element_by_partial_link_text('查 看').click()
    driver.implicitly_wait(20)
    driver.quit()
if __name__ == '__main__':
    getChrome()

先看下结果:

这就是跟requests的结果返回的一样的结果。对方服务器也知道了这是一个爬虫/自动化测试工具。

在浏览器页面中可以看到这样一行字:Chrome正受到自动测试软件的控制。

下面需要对selenium进行一些配置,隐藏webdriver 的信息,让服务器知道,我们是一个正常的人在操作。

解决方法可以参考这篇帖子:

https://blog.csdn.net/dslkfajoaijfdoj/article/details/109146051

版本2代码:

代码语言:javascript复制
from selenium.webdriver import Chrome
from selenium.webdriver.chrome.options import Options
# 使用selenium来进行测试
def getChrome():
    # 设置chrome参数
    chrome_options = Options()
    chrome_options.add_experimental_option("excludeSwitches", ["enable-automation"])
    chrome_options.add_experimental_option('useAutomationExtension', False)
    chrome_options.add_argument("--disable-blink-features=AutomationControlled")
    # 获取谷歌浏览器的启动
    driver = Chrome(options=chrome_options)
    # 打开链接
    driver.get("http://wsgg.sbj.cnipa.gov.cn:9080/tmann/annInfoView/annSearch.html?annNum=1739")
    driver.implicitly_wait(20)
    driver.find_element_by_partial_link_text('查 看').click()
    driver.implicitly_wait(20)
    driver.quit()
if __name__ == '__main__':
    getChrome()

再来看下效果:

现在我们可以使用selenium 打开这个网站点。但是在内容栏目中加载比较慢

这个结果的内容是用easyUI动态加载出来的。内容没加载出来,我们是无法进行下一步的。

所以这里在PyCharm运行的时候,要使用debug模式,来控制等待页面加载完成后在进行下一步。当然也可以自动添加上等待时间或者等待的某个内容加载完成,不过这里为了灵活,我们选择直接手动控制。

如果长时间没有加载出来,还可以直接在当前页面刷新操作。

但是经过了很长一段时间的等待,它一直卡着这个页面没有反应,说明easyui进行请求过程中被反爬机制监控了,如何验证这个问题,直接复制当面的页面在新的窗口中打开,看下是否能得到正确的结果。

这个问题怎么绕过呢,在当前的页面打开F12,开启开发者模式,打开后发现页面中还有debugger的存在,这里在开发者选项中禁用掉debugger(矩形按钮),然后点击继续(圆形按钮)。

然后继续在当前页面F5刷新,发现可以加载完成页面。

然后继续下一步,点击页面的debug,进行下一步, 执行点击【查看】的操作。

但是在页面中我们可以看到是这个效果:

图片并没有加载出来,network这中报了400的错误,说明还是被反爬拒绝了。

到底还是没有绕过??

前面第一步说明添加了两项chrome_options 的参数,可以打开原始页面,但是在继续访问动态加载出来的数据的时候就出问题,说明这个网站的反爬机制不仅仅是一点。

不太像政府机关类部门网站的作风,有点难搞了。

这个地方还是说明,这部操作有问题,服务器认为这不是人为的操作,那么在上面的那个 《selenium被识别的解决方法》的帖子中,还有第三个方法,

Selenium执行cdp命令 再次覆盖window.navigator.webdriver的值

能打开页面,在点击查看的时候被禁了,说明这个点击操作的问题。

版本3代码:

代码语言:javascript复制
from selenium.webdriver import Chrome
from selenium.webdriver.chrome.options import Options


# 使用selenium来进行测试
def getChrome():
    # 设置chrome参数
    chrome_options = Options()
    chrome_options.add_experimental_option("excludeSwitches", ["enable-automation"])
    chrome_options.add_experimental_option('useAutomationExtension', False)
    chrome_options.add_argument("--disable-blink-features=AutomationControlled")
    chrome_options.add_argument("Pragma=no-cache")
    # 获取谷歌浏览器的启动
    driver = Chrome(options=chrome_options)
    #Selenium执行cdp命令
    driver.execute_cdp_cmd("Page.addScriptToEvaluateOnNewDocument", {
        "source": """
        Object.defineProperty(navigator, 'webdriver', {
          get: () => undefined
        })
      """
    })
   
    # 打开链接
    driver.get("http://wsgg.sbj.cnipa.gov.cn:9080/tmann/annInfoView/annSearch.html?annNum=1739")
    driver.implicitly_wait(20)
    driver.find_element_by_partial_link_text('查 看').click()
    driver.implicitly_wait(20)
    driver.quit()
    
if __name__ == '__main__':
    getChrome()

代码运行后,还是同上面一样,并不见效,说明这个点不足以绕过反爬。

说明至少在某一个点上,被检测到了。那我们先手动跳过试一下。

发现这里只需要手动点击就没问题,可以正常刷新出来。

ok,那现在先用手动方法跳过这个反爬检测,然后继续后面的看下能否翻页。

版本4代码:

代码语言:javascript复制
import time

from selenium.webdriver import Chrome
from selenium.webdriver.chrome.options import Options


def getChrome():
    chrome_options = Options()
    # chrome_options.add_argument("--headless")
    # 增加一个参数设置,隐藏webdriver
    chrome_options.add_experimental_option("excludeSwitches", ["enable-automation"])
    chrome_options.add_experimental_option('useAutomationExtension', False)
    chrome_options.add_argument("--disable-blink-features=AutomationControlled")
    chrome_options.add_argument(
        'user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.106 Safari/537.36')
    chrome_options.add_argument("Pragma=no-cache")
    driver = Chrome(options=chrome_options)
    driver.execute_cdp_cmd("Page.addScriptToEvaluateOnNewDocument", {
        "source": """
        Object.defineProperty(navigator, 'webdriver', {
          get: () => undefined
        })
      """
    })

    driver.get("http://wsgg.sbj.cnipa.gov.cn:9080/tmann/annInfoView/annSearch.html?annNum=1739")
    driver.implicitly_wait(20)
    #   driver.find_element_by_partial_link_text('查 看').click()
    driver.implicitly_wait(20)
    links = []

    for i in range(50):
        print(i)
        time.sleep(1)
        try:
            links.extend(getNext(driver))
            links = list(set(links))
        except Exception as e:
            print(e)
            pass
    print("全部图片链接:")
    print(links)
    print(len(links))

#翻页 并且获取图片链接
def getNext(driver):
    driver.find_element_by_partial_link_text('下一页').click()
    driver.implicitly_wait(10)
    td_contents = driver.find_elements_by_tag_name('img')
    driver.implicitly_wait(10)
    links = []
    for img in td_contents:
        links.append(img.get_attribute("src"))
    print(links)
    return links


if __name__ == '__main__':
    getChrome()

这里能发现其实运行起来并没有什么效果,虽然代码在跑,但是不能翻页,说明,这还是被发现了,所以使用selenium点击并没有响应。

不过现在我们并不知道网站是靠什么检测的,我们就手动点击【查 看】使被检测某个参数正常,然后在进行下面的操作。

发现可以继续爬取了。

日志中有更新,而且页面可以实现自动翻页。

这样的可以通过笨办法实现绕过发爬,进行后续的操作。

后续:

到这里的话,可以实现爬虫功能了,后面的图片下载就是so easy,但是总是感觉不是很智能。

如果要实现全自动,那么就得需要知道,网站是靠什么来检测到这selenium工具而不是真实的人,这个是需要挨个尝试的,对方的反扒机制是什么。

问题算是解决了,但是感觉并不是很智能,不过这也是一个解决问题的策略。

如果非得要解决第一步这个问题,可以从下面几个点:

  1. 判断网站是通何种方式来区分正常浏览器还是测试工具
  2. 切换Chrome,换成Firefox等其他的内核的浏览器
  3. 换成低版本的Chrome

如果你有更好的解决方法,欢迎多多指教。

总结:

关于爬虫问题,基本的通用解决思路:

1.分析请求URL,是否存在动态加密,优先考虑使用requests

2.requests要设置好全部的请求头参数

3.一次性的爬虫不建议使用Scrapy,开发成本高

4.如有动态加密,短时间内不要考虑破解加密算法,太费劲

5.selenium使用起来会比较慢,优点是可解决动态js加密。

我是马拉松程序员,可不止于代码!

0 人点赞