题目四:爬虫-CSS反爬
分析题目:
(一)界面显示的117,在源码中是171。查看171元素对应的css源码。
可以看到,第一个1中left没有赋值。第二个7中left的值为1em,第三个1中left的值为-1em。
对【 1 7 1】进行移位,7的index+1em,第三个1的index-1em,结果为【1 1 7】,即为界面显示的数字。
(二)界面上显示141,源码中为6114。查看6114元素对应的css源码。
【 6 1 1 4 】,对6的index+1em-1em,对第一个1的index+2em,对第二个1的index-1em,对4的index-1em。【 6 1 4 1】,即后三位为界面显示的数值。对移位结果仅取后三位即可。
(三)界面上显示182,源码显示<div class=“txYyv21NCh”>:before</div>。
直接取得对应before的content内容即为界面上显示的内容。
从上面三个的分析可以总结出编码规律:
1.长度为3的,字体的顺序被打乱,根据left的赋值,调整位置即可。
2.长度为4的,基于1中的方法得到结果后,取得后面三位数值即为界面显示的值。
3.长度小于3的,直接在元素对应的before的content中找到该数值。
最开始的我,也以为分析完上面这些规律后,就可以正确的解答题目了,但跑完一次后,把答案提交,发现还是错误的。然后反复调试后,发现编码规律没那么简单。遗漏了以下场景:
(四)界面上显示116,源码显示<div class=“col-md-1”><div class=“eYcN0stlEG”>8</div> <div class=“CJZPE1XcUe”>:before</div></div>。
【8 116】,对8的index-1em会出现<0的现象,需要考虑index越界的问题。
(五)界面上显示97,源码显示997。
根据以上分析,该结果的获取过程中,同时还需要关注margin-right是否具有赋值。
所以编码规律还需要注意:
4.index是否越界。
5.同时需要关注margin-right是否具有赋值。
根据以上总结的规律,进一步就是编写代码解题啦(代码中存在部分冗余代码,可以进一步优化哦~)
#coding:utf-8 CSS反爬
import re
import asyncio,pyppeteer
from lxml import etree
class testMl:
def __init__(self):
self.total_num = 0
def detail_parse(self,div_html, response_str):
"""
:param div_html: 传进来的div[@class='col-md-1']
:param response_str: 该网页的elements内容,也就是response.text
:return:None
"""
# 获取div[@class='col-md-1']下的所有div标签
div_list = div_html.xpath("//div//div")
# todo:1.对于div_list长度为3以下的
# 就是使用伪元素选择器
if len(div_list) < 3:
number = [0]*len(div_list)
for i in range(len(div_list)):
text = div_list[i].xpath(".//text()")
if not text:
class_name = div_list[i].xpath(".//@class")[0]
#() 是为了提取匹配的字符串
num = re.findall(r"\.{}\:before\s*.*?\s*content\:\"(\d*)\"".format(class_name), response_str, re.S)[0]
self.total_num += int(num)
# print(num, "总数", self.total_num)
else:
class_name = div_list[i].xpath(".//@class")[0]
data = div_list[i].xpath(".//text()")[0]
left = re.findall(r"\.{}\s.*?\sleft\:(.*?)em".format(class_name), response_str)
margin_right = re.findall(r"\.{}\s.*?\smargin-right\:(.*?)em".format(class_name), response_str)
if not left and not margin_right:
# 如果left和margin_right为空,表名位置不需要调整
number[i] = data
# 否则就需要调整位置
else:
index = 0
if left:
index = i + int(left[0])
if margin_right:
index = i + int(margin_right[0])
if index >= 0 :#index大于等于0的时候才赋值
number[index] = data
num = ''
for j in range(len(number)):
num += str(number[j])
if int(num) !=0:
self.total_num += int(num)
# print(num, "总数", self.total_num)
# todo:2.对于div_list长度大于等于3的
# 长度为4就是其中有个div不显示,长度为3就是div标签数据显示顺序打乱
else:
if len(div_list) == 4:
div_list = div_list[1:]#只取后三位
number = [0]*len(div_list)
for i in range(0, len(div_list)):
div = div_list[i]
class_name = div.xpath(".//@class")[0]
data = div.xpath(".//text()")[0]
left = re.findall(r"\.{}\s.*?\sleft\:(.*?)em".format(class_name), response_str)
margin_right = re.findall(r"\.{}\s.*?\smargin-right\:(.*?)em".format(class_name), response_str)
if not left and not margin_right:
# 如果left和margin_right为空,表名位置不需要调整
number[i] = data
# 否则就需要调整位置
else:
index = 0
if left:
index = i + int(left[0])
if margin_right:
index = i + int(margin_right[0])
if index >= 0:
number[index] = data
num = ''
for k in range(len(number)):
num += str(number[k])
self.total_num += int(num)
# print(num, "总数", self.total_num)
def print_total(self):
print("总数", self.total_num)
async def main():
brower = await pyppeteer.launch(headless=False)
page = await brower.newPage()
Ml = testMl()
await page.setUserAgent(
'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3314.0 Safari/537.36 SE 2.X MetaSr 1.0')
await page.setJavaScriptEnabled(enabled=True)
await page.evaluate(
'''() =>{ Object.defineProperties(navigator,{ webdriver:{ get: () => false } }) }''')
await page.goto("http://www.glidedsky.com/level/web/crawler-css-puzzle-1")
# print(await page.content())
address = "123456789@qq.com"
passwd = "******"
await page.type('#email', address)
await page.type("#password", passwd)
button = (await page.xpath('//*[@id="app"]/main/div[1]/div/div/div/div[2]/form/div[4]/div/button'))[0]
await button.click()
await asyncio.sleep(2.0)
for i in range(1,1001):
await page.goto("http://www.glidedsky.com/level/web/crawler-css-puzzle-1?page={0}".format(i))
# print(i)
content = await page.content()
tree_content = etree.HTML(content)
for k in range(1,13):
col_md = tree_content.xpath('//*[@id="app"]/main/div[1]/div/div/div/div[{0}]'.format(k))[0]
col_md = etree.tostring(col_md)
col_md_html = etree.HTML(col_md)
# print(col_md_html)
Ml.detail_parse(col_md_html,content)
# await asyncio.sleep(2.0)
await brower.close()
Ml.print_total()
loop = asyncio.get_event_loop()
loop.run_until_complete(main())
总的来说,反爬的技巧在于分析编码的规律,找到规律,也就能够解题了~