本文将说说另一种常见的反爬解决方案,即:“CSS偏移”。CSS偏移反爬就是利用“CSS样式”对网页元素进行自定义排序,最终让网页显示正确的数据。下面用一个简单的例子来说明CSS偏移网站的一般解决方案。目标对象:aHR0cDovL3d3dy5wb3J0ZXJzLnZpcC9jb25mdXNpb24vZmxpZ2h0Lmh0bWw=1。分析并打开目标网站,在开发者工具面板查看“机票价格”的网页元素构成。我们发现机票价格从上下两个区域的数据元素中偏移了一定的偏移量,最后显示在页面上。以第一个数据为例,实际票价为467,区域1的宽度设置为48px,left的值为-48px,表示左边距向左偏移48px。内部i标签的宽度为16px,完全占据了父容器的宽度。即:如果隐藏区域二,票价应该是777。我们继续看区域2内容中的第一个b标签,内容为6,left属性值为-32px,宽度为16px,这将涵盖上面的第二个数字。第二个b标签,内容为4,left属性值为-48px,宽度也是16px,会覆盖上面第一个数字。所以最后一个网页显示的机票价格是467。二、特殊处理如果仔细观察网页的元素,会发现第二行显示的是b元素下的第三个i标签,不是与前面两个i标签显示相同。实际上,这是因为i元素标签将显示样式设置为inline-block。PS:inline-block默认元素之间会有一定的间隙。因此,为了正确解析出数据,我们需要对网页源代码的部分网页元素进行二次更新。3、实战首先,我们需要安装依赖包。#依赖包#bs4用于两次更新网页源码的元素样式pip3installbeautifulsoup4#lxml用于爬取网页数据pip3installlxml接下来我们使用bs4解析网页源码,得到所有em元素,并在下方修改“b标签”的显示属性值为浮动“flex”,然后重新导出数据。importrequestsfrombs4importBeautifulSoup...url='http://.../flight.html'resp=requests.get(url).text#首先解析源码soup=BeautifulSoup(resp,"lxml")#查询页面em_elements中的em元素=soup.find_all("em",class_="rel")#为em_elements中的em_element的第一个b标签添加flex属性:first_b_element=em_element.find_all('b')[0]#添加flex属性first_b_element['style']=first_b_element['style']+';display:flex;'#重新导出数据分析resp=soup.prettify()...#写入本地文件视图#打开('temp.html','w',encoding='utf-8')asfile:#file.write(resp)...接下来我们使用xpath语法获取所有flightItem元素控件。结合正则表达式得到机票价格对应的元素的左偏移量,根据这个偏移量计算数据应该显示的位置索引。最后根据索引,将数据放在列表的预定位置,形成真实的机票价格。importrefromlxmlimportetree...#数据分析html=etree.HTML(resp)#查询多少航班数据div_list=html.xpath('//div[@class="leftcol-md-9"]/div')print('Numberofflightdata:',len(div_list))forindexinrange(len(div_list)):#获取所有b标签b_elements=div_list[index].xpath('.//em/b')#从第一个b标签下的子标签数据中,可以得到价格的位数(3位,4位)price_num_list=b_elements[0].xpath('./i/text()')print("Thepriceofthebaseticketis:",''.join([item.strip()foriteminprice_num_list]))#从第二个b标签开始,获取index的真实价格对应的数字,b_elementinenumerate(b_elements[1:]):#Dataprice_num=int(b_element.xpath('./text()')[0])#获取b标签的style属性值style=b_element.xpath('./@style')[0]#使用正则表达式获取左边的属性值left_value=re.findall('left:(.*?)px',style)[0]#根据左边的值,计算出指数数据在真实价格中的位置(-1/-2/-3)price_index=int(int(left_value)/16)#替换源数组中的数据,并根据索引设置值price_num_list[price_index]=price_num#将所有item转成字符串,合成一个新数组price_num_list=[str(item).strip()foriteminprice_num_list]#Compositionpriceprice=int(''.join(price_num_list))print("Ticketprice:",price)...
