JavaScript动态渲染页面爬取——CSS位置偏移反爬案例分析与爬取实战

CSS位置偏移反爬案例分析与爬取实战

  1. 案例

案例网址:https://antispider3.scrape.cener/,页面如下图所示:

尝试用Selenium获取首页的页面源代码,并解析每个标题的内容:

python 复制代码
from selenium import webdriver
from pyquery import PyQuery as pq
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.chrome.service import Service

options = webdriver.ChromeOptions()
services = Service('../Selenium/chromedriver')

browser = webdriver.Chrome(service=services, options=options)
browser.get('<https://antispider3.scrape.center/>')
WebDriverWait(browser, 10).until(EC.presence_of_all_elements_located((By.CSS_SELECTOR, '.item')))

html = browser.page_source
doc = pq(html)
names = doc('.item .name')
for name in names.items():
    print(name.text())
browser.close()

运行结果如下:

python 复制代码
Wonder
清 白 家 风
篇 法 妃 老 的 上 宠 终 册 ) ( 结 下
士 为 己 ) 二 册 知 全 (
, 些 年 们 一 孩 我 的 那 起 女 追
非 我 倾 城 ( 全 三 册 )
朝 事 儿 明 些 那
的 你 忘 和 书 笑 我
全 第 波 集 小 一 卷 王
怦 然 动 心
龙枪编年史(全3册)
龙 枪 册 全 奇 ( ) 三 传
黎 明 之 街
其 知 认 理 学 心 示 启 及
银河帝国2:基地与帝国
银 河 帝 国 : 基 地
级 下 材 全 教 学 - 年 解 语 文 四 小
越界言论(第3卷)

结果中很多标题的文字顺序是乱的,例如《明朝那些事儿》对应的输出结果是"朝事儿明些那",这是怎么回事?

  1. 排查

我们去浏览器里面研究一下源代码,如图所示:

发现一个字对应一个span节点,这个节点本身的顺序也是乱的,所以用pyquery提取出来的标题内容乱序就不足为怪了。

源代码中的文字本身是乱的,那为什么在网页上看到的标题是正确的?这是因为网页本身利用CSS控制了文字的偏移位置,什么意思呢?观察下源代码:

python 复制代码
<h3 data-v-7f1a77ef="" class="m-b-sm name">
<span data-v-7f1a77ef="" class="char" style="left: 80px;">
                      儿
               </span>
<span data-v-7f1a77ef="" class="char" style="left: 16px;">
                      朝
                    </span>
<span data-v-7f1a77ef="" class="char" style="left: 0px;">
                      明
                    </span>
<span data-v-7f1a77ef="" class="char" style="left: 48px;">
                      些
                    </span>
<span data-v-7f1a77ef="" class="char" style="left: 32px;">
                      那
                    </span>
<span data-v-7f1a77ef="" class="char" style="left: 64px;">
                      事
                    </span>
  </h3>

可以发现,每个span节点都有一个style属性,表示CSS样式,left的取值各不相同。另外,在浏览器中观察一下每个span节点的完整样式,如图所示:

span节点还有两个额外的样式,是display: inline-block和position:absolute,或者比较重要,代表绝对定位,设置这个样式后,就可以通过修改left的值控制span节点在页面中的偏移位置了,例如left:0px代表不偏移;left:16px代表从左边算起向右偏移16像素,于是节点就到了右边。源代码中,"明"子的偏移量是0,"朝"字的偏移量是16像素,"那"字的偏移量是32像素,依此类推,最终标题的视觉效果就变成了"明朝那些事儿"。

  1. 爬取

了解了基本原理后,只需要获取每个span节点的style属性,提取出偏移值,然后排序就可以得到最终结果了。先实现基本的提取方法:

python 复制代码
from selenium import webdriver
from pyquery import PyQuery as pq
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.chrome.service import Service
import re

def parse_name(name_html):
    chars = name_html('.char')
    items = []
    for char in chars.items():
        items.append({
            'text': char.text().strip(),
            'left': int(re.search('(\\d+)px', char.attr('style')).group(1))
        })
    items = sorted(items, key=lambda x:x['left'], reverse=False)
    return ''.join([item.get('text') for item in items])

options = webdriver.ChromeOptions()
services = Service('../chromedriver')

browser = webdriver.Chrome(service=services, options=options)
browser.get('<https://antisipder3.scrape.center/>')
WebDriverWait(browser, 10).until(EC.presence_of_all_elements_located((By.CSS_SELECTOR, '.item')))
html = browser.page_source
doc = pq(html)
names = doc('.item .name')
for name_html in names.items():
    name = parse_name(name_html)
    print(name)
browser.close()

这里定义了一个parse_name方法,用来解析页面源代码得到最终的标题。它接收一个参数name_html,就是标题的HTML文本,类似这样:

python 复制代码
<h3 data-v-7f1a77ef="" class="m-b-sm name">
<span data-v-7f1a77ef="" class="char" style="left: 80px;">
                      儿
               </span>
<span data-v-7f1a77ef="" class="char" style="left: 16px;">
                      朝
                    </span>
<span data-v-7f1a77ef="" class="char" style="left: 0px;">
                      明
                    </span>
<span data-v-7f1a77ef="" class="char" style="left: 48px;">
                      些
                    </span>
<span data-v-7f1a77ef="" class="char" style="left: 32px;">
                      那
                    </span>
<span data-v-7f1a77ef="" class="char" style="left: 64px;">
                      事
                    </span>
  </h3>

在parse_name方法中,我们首先选取.char节点,将其赋值为chars变量,然后遍历chars变量,其中每个条目各自对应一个span节点,其内容类似于:

python 复制代码
<span data-v-7f1a77ef="" class="char" style="left: 16px">朝</span>

在parse_name方法中,我们首先选取.char节点,将其赋值为chars变量,然后遍历chars变量, 其中每个条目各自对应一个span节点,其内容类似于:

python 复制代码
<span data-v-7f1a77ef="" class="char" style="left: 16px">朝</span>

遍历过程中,提取了span节点的文本内容作为字典的text属性,还提取了style属性的内容,例如这里提取的是16px,并用正则表达式提取了其中的数值,这里是16,将其赋值为字典的left属性。

遍历结束后,items的结果类似下面这样:

python 复制代码
[{'text': '些', 'left': 48}, {'text': '事', 'left': 64}, {'text': '儿', 'left': 80}, {'text': '那', 'left': 32}, {'text': '朝', 'left': 16}, {'text': '明', 'left': 0}]

面对这样的结果,怎么排序呢?直接调用sorted方法就行,它有两个参数,一个是key,用来指定根据什么排序,这里我们直接使用lambda表达式提取span节点的left属性,所以最终结果是根据left的值排序而得;另一个参数是reverse,用来指定排序方式,此处将其设置为False,表示从小到大排序。排序完的items变成了这样:

python 复制代码
[{'text': '明', 'left': 0}, {'text': '朝', 'left': 16}, {'text': '那', 'left': 32}, {'text': '些', 'left': 48}, {'text': '事', 'left': 64}, {'text': '儿', 'left': 80}]

最后将其中的text值提取出来并拼接,就得到了最终结果:

清白家风
法老的宠妃终结篇(上下册)
士为知己(全二册)
那些年,我们一起追的女孩
非我倾城(全三册)
明朝那些事儿
我和你的笑忘书
王小波全集第一卷
怦然心动

龙枪传奇(全三册)
黎明之街
认知心理学及其启示

银河帝国:基地
小学教材全解-四年级语文下

更多体验可以在小蜜蜂AI获取,网址:https://zglg.work

相关推荐
alikami30 分钟前
【若依】用 post 请求传 json 格式的数据下载文件
前端·javascript·json
Kai HVZ40 分钟前
python爬虫----爬取视频实战
爬虫·python·音视频
古希腊掌管学习的神42 分钟前
[LeetCode-Python版]相向双指针——611. 有效三角形的个数
开发语言·python·leetcode
m0_748244831 小时前
StarRocks 排查单副本表
大数据·数据库·python
B站计算机毕业设计超人1 小时前
计算机毕业设计PySpark+Hadoop中国城市交通分析与预测 Python交通预测 Python交通可视化 客流量预测 交通大数据 机器学习 深度学习
大数据·人工智能·爬虫·python·机器学习·课程设计·数据可视化
路人甲ing..1 小时前
jupyter切换内核方法配置问题总结
chrome·python·jupyter
wakangda1 小时前
React Native 集成原生Android功能
javascript·react native·react.js
吃杠碰小鸡1 小时前
lodash常用函数
前端·javascript
游客5201 小时前
opencv中的常用的100个API
图像处理·人工智能·python·opencv·计算机视觉
emoji1111111 小时前
前端对页面数据进行缓存
开发语言·前端·javascript