打开Python爬虫实战练习C02页面 爬虫实战练习C02,看到页面上是一个拖拽式的验证码,下面有一行提示"拖动滑块验证码成功后加载数据。。。"。
这时候先按快捷键 F12 打开浏览器开发者工具,切换到 Network 标签,然后尝试着拖动验证码。此时会发现把验证码拖到最右边后,页面加载了一个数据表格出来,里面是一些广州到各大热门城市的机票价格。
我们点击一下页面上的"立即验证",先看看这一个爬虫案例是要求我们获取并计算什么数据。打开后发现是让我们计算机票的平均价格。
鉴于刚才我们拖动验证码到数据加载完毕,都没有在开发者工具中看到有新的 HTTP 请求发出,所以可以断定数据早已经随着页面加载到了浏览器,只不过验证码通过后才动态渲染到页面上。
此时就可以通过右键菜单打开网页源码查看,可以看到 html 源码从第129行开始是一些 javascript 代码,继续往下查看,在第169行的位置看到一长串字符,根据以往的经验猜测这是一串 Base64 编码的字符串,把它复制出来验证一下。
打开在线工具 Base64编码解码,把刚刚复制的字符串粘贴进去,点击"解密"按钮,可以看到解码出来的是一个 Json 格式的内容,与页面上的内容核对一下,发现这里就是页面上的内容。虽然这些内容混合在 javascript 代码里面,但我们可以通过关键字 encryptedData 等把它截取出来再使用 Python 的 Base64 库进行解码,然后取出机票价格这一列计算平均值。
这时候就可以直接编写爬虫代码了:
html = requests.get(base_url, headers=myheaders).text
# 这是17 是 encryptedData = 的长度
a = html.index('encryptedData = "') + 17
html = html[a:]
b = html.index('";')
html = html[:b]
print(html)
dic = eval(base64.b64decode(html.encode('utf-8')))
objs = dic['flights']
prices = []
for obj in objs:
print(obj)
prices.append(obj['price'])
print(prices)
print(np.mean(prices))
得出的值是617,回到刚才打开的验证页面,填入这个值并点击提交答案,提示答对了。
然而,这还没完。
这次是碰巧遇到 javascript 没有加密混淆,但实际上是没这么好的事的,既然这里有拖拽式验证码,那我们就尝试一下使用 selenium 来开发一个爬虫。
selenium 是可以操控 html 元素进行拖动的操作的,但我们需要先知道要拖动的元素的 ID 或者 CSS Selector。回到我们的目标页面,使用开发者工具 Elements 的箭头指向验证码滑块的位置,并在 Elements 查看它的 ID 之类的信息,并在滑块拖动成功后查看它的最终位置。
这个位置信息在 Elements 的箭头选中滑块的情况下,在开发者工具右边的 styles 就能看到。
252px 就是我们要拖到的位置,然后调用 selenium 调试一下我们的代码。
client = webdriver.Chrome()
client.get(url)
time.sleep(10)
# 事件参数对象
actionChains = ActionChains(client)
# 捕捉滑块元素
slide_btn = client.find_element(By.ID, 'slider')
# 观察网站滑块移动的长度和位置
actionChains.click_and_hold(slide_btn)
actionChains.move_by_offset(252,0)
actionChains.release()
actionChains.perform()
html = client.page_source
print(html)
执行完上面的代码后,发现并没有像我们预期地那样出来数据,这说明页面的 javascript 代码时肯定有反爬虫逻辑,回到网页源码第129行开始分析一下这段 javascript 代码。
因为数据在第169行,我们直接找到数据所在的函数(第152 - 184行)进行分析,可以看到函数开始就是判断验证码滑块的位置,直到第165行有一个判断,回头找到这个 x_map 的变量定义,发现它是一个字典的数据结构,并且是在 mousemove 事件里面赋值的,在鼠标拖动时把光标的 X 轴坐标值存了起来。
熟悉编程的朋友们都知道,字典是由键 - 值 对组成的,而且键是唯一的,也就是说你存多次同一个键进去,尽管值不同,但字典里也只有一个键值对。
x_map < 2 就是判断了鼠标触发了多少次 mousemove 事件并被记录下来。说白了就是我们上面的代码一次拖到底,x_map 的长度只有 1,所以是小于2的。
知道了原理,我们就可以改进一下上面的代码,把鼠标拖动的事件分成多次就好了。
client = webdriver.Chrome()
client.get(url)
time.sleep(10)
# 事件参数对象
actionChains = ActionChains(client)
# 捕捉滑块元素
slide_btn = client.find_element(By.ID, 'slider')
# 观察网站滑块移动的长度和位置
actionChains.click_and_hold(slide_btn)
actionChains.move_by_offset(220,0)
# 这里要注意:
# 以下三个是以上面的坐标(220,0)为起点来计算的
# 所以最终移动的距离是220加上以下的累计
actionChains.move_by_offset(11,0)
actionChains.move_by_offset(13,0)
actionChains.move_by_offset(10,0)
actionChains.release()
actionChains.perform()
html = client.page_source
print(html)
这个时候我们就能在 selenium 界面看到数据成功加载出来了,接下来就是使用 xpath 把机票价格获取出来并计算平均值即可。
完整的爬虫案例代码:爬虫案例代码