字体反爬案例分析与爬取实战

简介: 字体反爬案例分析与爬取实战

字体反爬案例分析与爬取实战
该案例将真实的数据隐藏到字体文件里,即使获取了页面源代码,也没法直接提取数据的真实值。

案例介绍
案例网站https://antispider4.scrape.center/,爬取电影标题、类别、评分等,代码实现如下:

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://antispider4.scrape.center/>')
WebDriverWait(browser, 10).until(EC.presence_of_all_elements_located((By.CSS_SELECTOR, '.item')))

html = browser.page_source
doc = pq(html)
items = doc('.item')
for item in items.items():
    name = item('.name').text()
    categories = [o.text() for o in item('.categories button').items()]
    score = item('.score').text()
    print(f'name:{name} categories: {categories} score: {score}')
browser.close()

先用Selenium打开案例网站,等待所有电影加载出来,然后获取页面源代码,并通过pyquery提取和解析每一个电影的信息,得到名称、类别和评分,之后输出,运行结果如下:

name:霸王别姬 - Farewell My Concubine categories: ['剧情', '爱情'] score: 
name:这个杀手不太冷 - Léon categories: ['剧情', '动作', '犯罪'] score: 
name:肖申克的救赎 - The Shawshank Redemption categories: ['剧情', '犯罪'] score: 
name:泰坦尼克号 - Titanic categories: ['剧情', '爱情', '灾难'] score: 
name:罗马假日 - Roman Holiday categories: ['剧情', '喜剧', '爱情'] score: 
name:唐伯虎点秋香 - Flirting Scholar categories: ['喜剧', '爱情', '古装'] score: 
name:乱世佳人 - Gone with the Wind categories: ['剧情', '爱情', '历史', '战争'] score: 
name:喜剧之王 - The King of Comedy categories: ['剧情', '喜剧', '爱情'] score: 
name:楚门的世界 - The Truman Show categories: ['剧情', '科幻'] score: 
name:狮子王 - The Lion King categories: ['动画', '歌舞', '冒险'] score:

很奇怪,结果中的score字段不包含任何信息,怎么回事?观察分析,对应的源代码并不包含数字信息,如图所示:
image.png

span节点就是什么信息都没有,提取不出来自然也不足为奇了,那页面上的评分结果是怎么显示出来的呢?

其实也是CSS的原因。

案例分析
观察源码,各个span节点的不同之处在于内部i节点的class取值不太一样。可以看到一共有3个span节点,对应的class取值分别为icon-789、icon-981、icon-504,这和评分9.5有啥关系呢?

image.png

发现i节点内部有一个::before字段,在CSS中,该字段用于创建一个伪节点,即这个节点和i节点或者span节点不一样。::before可以往特定的节点中插入内容,同时在CSS中使用content字段定义这个内容。第一个i节点看到了9,另外两个i节点,看到.和5,组合起来为9.5。

实战
那class取值和content字段值的映射关系是怎么定义的?可以在浏览器中追踪CSS源代码,代码如下图所示:
image.png

进入文件后,可以看到这个CSS源代码都在一行放着,点击“{}”按钮格式化代码,如图所示:

image.png

可以从中找出如下内容:

.icon-437:before {
   
    content: "3"
}

.icon-378:before {
   
    content: "4"
}

.icon-504:before {
   
    content: "5"
}

.icon-203:before {
   
    content: "6"
}

.icon-102:before {
   
    content: "7"

原来class对应的值就是一个个评分结果。这样我们只要解析对应的结果再做转换即可。这里需要读取CSS文件并提取映射关系,这个CSS文件是https://antispider4.scrape.center/css/app.654ba59e.css,其部分内容如图所示:

image.png

我们可以试着用requests库读取结果,并通过正则表达式将映射关系提取出来,代码如下:

import re
import requests
url = '<https://antispider4.scrape.center/css/app.654ba59e.css>'

response = requests.get(url)
pattern = re.compile('.icon-(.*?):before\\{content:"(.*?)"\\}')
results = re.findall(pattern, response.text)
icon_map = {
   item[0]: item[1] for item in results}

这里首先使用requests库提取了CSS文件的内容,然后使用正则表达式进行文本匹配,表达式写作.icon-(.?):before{content:”(.?)”},这个表达式并没有考虑空格,因为CSS源代码本身就是一行放着而去去除了所有空格。

例如,对于如下CSS样式:

.icon-789:before{
   content:"9"}

就会提取得到两个group,第一个是789,第二个是9。

这里使用re里的findall方法进行内容匹配,得到结果如下:

[....., ('asterisk', '*'), ('plus', '+'), ('comma', ','), ('hyphen', '-'), ('981', '.'), ('slash', '/'), ('272', '0'), ('643', '1'), ('180', '2'), ('437', '3'), ('378', '4'), ('504', '5'), ('203', '6'), ('102', '7'), ('281', '8'), ('789', '9'), ('colon', ':'), ('semicolon', ';'), ('less', '<'), ('equal', '='), ('greater', '>'), ('question', '?'), ('at', '@'), ('A', 'A'), ('B', 'B'), ('C', 'C'), ('D', 'D'), ('E', 'E'), ('F', 'F'), ('G', 'G'), ('H', 'H'), ('I', 'I'), ('J', 'J'), ('K', 'K'), ('L', 'L'), ('M', 'M'), ('N', 'N'), ('O', 'O'), ('P', 'P'), ('Q', 'Q'), ('R', 'R'), ('S', 'S'), ('T', 'T'), ('U', 'U'), ('V', 'V'), ('W', 'W'), ('X', 'X'), ('Y', 'Y'), ('Z', 'Z'), ('bracketleft', '['), ('backslash', '\\\\\\\\'), ('bracketright', ']'), ('asciicircum', '^'), ('underscore', '_'), ('grave', '`'), ('a', 'a'), ('b', 'b'), ('c', 'c'), ('d', 'd'), ('e', 'e'), ('f', 'f'), ('g', 'g'), ('h', 'h'), ('i', 'i'), ('j', 'j'), ('k', 'k'), ('l', 'l'), ('m', 'm'), ('n', 'n'), ('o', 'o'), ('p', 'p'), ('q', 'q'), ('r', 'r'), ('s', 's'), ('t', 't'), ('u', 'u'), ('v', 'v'), ('w', 'w'), ('x', 'x'), ('y', 'y'), ('z', 'z'), ('braceleft', '{'), ('bar', '|'), ('braceright', '}'), ('asciitilde', '~'), ('Adieresis', '\\\\80,\\\\C4'), ('Aring', '\\\\81,\\\\C5'), ('Ccedilla', '\\\\82,\\\\C7'), ('Eacute', '\\\\83,\\\\C9'), ('Ntilde', '\\\\84,\\\\D1'), ('Odieresis', '\\\\85,\\\\D6'), ('Udieresis', '\\\\86,\\\\DC'), ('aacute', '\\\\87,\\\\E1'), ('agrave', '\\\\88,\\\\E0'), ('acircumflex', '\\\\89,\\\\E2'), ('adieresis', '\\\\8A,\\\\E4'), ('atilde', '\\\\8B,\\\\E3'), ('aring', '\\\\8C,\\\\E5'), ('ccedilla', '\\\\8D,\\\\E7'), ('eacute', '\\\\8E,\\\\E9'), ('egrave', '\\\\8F,\\\\E8'), ('ecircumflex', '\\\\90,\\\\EA'), ('edieresis', '\\\\91,\\\\EB'), ('iacute', '\\\\92,\\\\ED'), ('igrave', '\\\\93,\\\\EC'), ('icircumflex', '\\\\94,\\\\EE'), ('idieresis', '\\\\95,\\\\EF'), ('ntilde', '\\\\96,\\\\F1'), ('oacute', '\\\\97,\\\\F3'), ('ograve', '\\\\98,\\\\F2'), ('ocircumflex', '\\\\99,\\\\F4'), ('odieresis', '\\\\9A,\\\\F6'), ('otilde', '\\\\9B,\\\\F5'), ('uacute', '\\\\9C,\\\\FA'), ('ugrave', '\\\\9D,\\\\F9'), ('ucircumflex', '\\\\9E,\\\\FB'), ('udieresis', '\\\\9F,\\\\FC'), ('dagger', '\\\\2020'), ('degree', '\\\\B0'), ('cent', '\\\\A2'), ('sterling', '\\\\A3'), ('section', '\\\\A7'), ('bullet', '\\\\2022'), ('paragraph', '\\\\B6'), ('germandbls', '\\\\DF'), ('registered', '\\\\AE'), ('copyright', '\\\\A9'), ('trademark', '\\\\2122'), ('acute', '\\\\B4'), ('dieresis', '\\\\A8'), ('notequal', '\\\\AD,\\\\2260'), ('AE', '\\\\C6'), ('Oslash', '\\\\D8'), ('infinity', '\\\\221E'), ('plusminus', '\\\\B1'), ('lessequal', '\\\\2264'), ('greaterequal', '\\\\2265'), ('yen', '\\\\A5'), ('mu', '\\\\B5'), ('partialdiff', '\\\\2202'), ('summation', '\\\\2211'), ('product', '\\\\220F'), ('pi', '\\\\3C0'), ('integral', '\\\\222B'), ('ordfeminine', '\\\\AA'), ('ordmasculine', '\\\\BA'), ('Omega', '\\\\2126'), ('ae', '\\\\E6'), ('oslash', '\\\\F8'), ('questiondown', '\\\\BF'), ('exclamdown', '\\\\A1'), ('logicalnot', '\\\\AC'), ('radical', '\\\\221A'), ('florin', '\\\\192'), ('approxequal', '\\\\2248'), ('Delta', '\\\\2206'), ('guillemotleft', '\\\\AB'), ('guillemotright', '\\\\BB'), ('ellipsis', '\\\\2026'), .....]

这个结果由很多二元组组成的列表。我们遍历这个列表,将其赋值成字典即可,最后icon_map就变成了如下这样:

{
   
...
'at':'@',
'A':'A',
'B':'B',
....
'789':'9',
....
'bar':'|',
...
)

例如使用789索引,得到结果就是9。

运行测试一下:

print(icon_map['789'])
print(icon_map['437'])

运行结果:
9
3

和源代码保持一致。

所以,只需要修改一下提取逻辑即可,实现代码如下:

import re
import requests
from selenium.webdriver.support.wait import WebDriverWait
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.chrome.service import Service
from selenium import webdriver
url = '<https://antispider4.scrape.center/css/app.654ba59e.css>'

response = requests.get(url)
pattern = re.compile('.icon-(.*?):before\\{content:"(.*?)"\\}')
results = re.findall(pattern, response.text)
icon_map = {
   item[0]: item[1] for item in results}

def parse_score(item):
    elements = item('.icon')
    icon_values = []
    for element in elements.items():
        class_name = (element.attr('class'))
        icon_key = re.search('icon-(\\d+)', class_name).group(1)
        icon_value = icon_map.get(icon_key)
        icon_values.append(icon_value)
    return ''.join(icon_values)

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

browser = webdriver.Chrome(service=services, options=options)

browser.get('<https://antispider4.scrape.center/>')
WebDriverWait(browser, 10).until(EC.presence_of_all_elements_located((By.CSS_SELECTOR, '.item')))
html = browser.page_source
doc = pq(html)
items = doc('.item')
for item in items.items():
    name = item('.name').text()
    categories = [o.text() for o in item('.categories button').items()]
    score = parse_score(item)
    print(f'name:{name} categories: {categories} score:{score}')
browser.close()

运行结果如下:

name:霸王别姬 - Farewell My Concubine categories: ['剧情', '爱情'] score:9.5
name:这个杀手不太冷 - Léon categories: ['剧情', '动作', '犯罪'] score:9.5
name:肖申克的救赎 - The Shawshank Redemption categories: ['剧情', '犯罪'] score:9.5
name:泰坦尼克号 - Titanic categories: ['剧情', '爱情', '灾难'] score:9.5
name:罗马假日 - Roman Holiday categories: ['剧情', '喜剧', '爱情'] score:9.5
name:唐伯虎点秋香 - Flirting Scholar categories: ['喜剧', '爱情', '古装'] score:9.5
name:乱世佳人 - Gone with the Wind categories: ['剧情', '爱情', '历史', '战争'] score:9.5
name:喜剧之王 - The King of Comedy categories: ['剧情', '喜剧', '爱情'] score:9.5
name:楚门的世界 - The Truman Show categories: ['剧情', '科幻'] score:9.0
name:狮子王 - The Lion King categories: ['动画', '歌舞', '冒险'] score:9.0
相关文章
|
3天前
|
人工智能 自然语言处理 Shell
深度评测 | 仅用3分钟,百炼调用满血版 Deepseek-r1 API,百万Token免费用,简直不要太爽。
仅用3分钟,百炼调用满血版Deepseek-r1 API,享受百万免费Token。阿里云提供零门槛、快速部署的解决方案,支持云控制台和Cloud Shell两种方式,操作简便。Deepseek-r1满血版在推理能力上表现出色,尤其擅长数学、代码和自然语言处理任务,使用过程中无卡顿,体验丝滑。结合Chatbox工具,用户可轻松掌控模型,提升工作效率。阿里云大模型服务平台百炼不仅速度快,还确保数据安全,值得信赖。
157353 24
深度评测 | 仅用3分钟,百炼调用满血版 Deepseek-r1 API,百万Token免费用,简直不要太爽。
|
5天前
|
人工智能 API 网络安全
用DeepSeek,就在阿里云!四种方式助您快速使用 DeepSeek-R1 满血版!更有内部实战指导!
DeepSeek自发布以来,凭借卓越的技术性能和开源策略迅速吸引了全球关注。DeepSeek-R1作为系列中的佼佼者,在多个基准测试中超越现有顶尖模型,展现了强大的推理能力。然而,由于其爆火及受到黑客攻击,官网使用受限,影响用户体验。为解决这一问题,阿里云提供了多种解决方案。
16986 37
|
13天前
|
机器学习/深度学习 人工智能 自然语言处理
PAI Model Gallery 支持云上一键部署 DeepSeek-V3、DeepSeek-R1 系列模型
DeepSeek 系列模型以其卓越性能在全球范围内备受瞩目,多次评测中表现优异,性能接近甚至超越国际顶尖闭源模型(如OpenAI的GPT-4、Claude-3.5-Sonnet等)。企业用户和开发者可使用 PAI 平台一键部署 DeepSeek 系列模型,实现 DeepSeek 系列模型与现有业务的高效融合。
|
5天前
|
并行计算 PyTorch 算法框架/工具
本地部署DeepSeek模型
要在本地部署DeepSeek模型,需准备Linux(推荐Ubuntu 20.04+)或兼容的Windows/macOS环境,配备NVIDIA GPU(建议RTX 3060+)。安装Python 3.8+、PyTorch/TensorFlow等依赖,并通过官方渠道下载模型文件。配置模型后,编写推理脚本进行测试,可选使用FastAPI服务化部署或Docker容器化。注意资源监控和许可协议。
1310 8
|
13天前
|
人工智能 搜索推荐 Docker
手把手教你使用 Ollama 和 LobeChat 快速本地部署 DeepSeek R1 模型,创建个性化 AI 助手
DeepSeek R1 + LobeChat + Ollama:快速本地部署模型,创建个性化 AI 助手
3416 117
手把手教你使用 Ollama 和 LobeChat 快速本地部署 DeepSeek R1 模型,创建个性化 AI 助手
|
8天前
|
人工智能 自然语言处理 API
DeepSeek全尺寸模型上线阿里云百炼!
阿里云百炼平台近日上线了DeepSeek-V3、DeepSeek-R1及其蒸馏版本等六款全尺寸AI模型,参数量达671B,提供高达100万免费tokens。这些模型在数学、代码、自然语言推理等任务上表现出色,支持灵活调用和经济高效的解决方案,助力开发者和企业加速创新与数字化转型。示例代码展示了如何通过API使用DeepSeek-R1模型进行推理,用户可轻松获取思考过程和最终答案。
|
5天前
|
人工智能 自然语言处理 程序员
如何在通义灵码里用上DeepSeek-V3 和 DeepSeek-R1 满血版671B模型?
除了 AI 程序员的重磅上线外,近期通义灵码能力再升级全新上线模型选择功能,目前已经支持 Qwen2.5、DeepSeek-V3 和 R1系列模型,用户可以在 VSCode 和 JetBrains 里搜索并下载最新通义灵码插件,在输入框里选择模型,即可轻松切换模型。
934 14
|
12天前
|
API 开发工具 Python
阿里云PAI部署DeepSeek及调用
本文介绍如何在阿里云PAI EAS上部署DeepSeek模型,涵盖7B模型的部署、SDK和API调用。7B模型只需一张A10显卡,部署时间约10分钟。文章详细展示了模型信息查看、在线调试及通过OpenAI SDK和Python Requests进行调用的步骤,并附有测试结果和参考文档链接。
1938 9
阿里云PAI部署DeepSeek及调用
|
9天前
|
人工智能 数据可视化 Linux
【保姆级教程】3步搞定DeepSeek本地部署
DeepSeek在2025年春节期间突然爆火出圈。在目前DeepSeek的网站中,极不稳定,总是服务器繁忙,这时候本地部署就可以有效规避问题。本文以最浅显易懂的方式带读者一起完成DeepSeek-r1大模型的本地部署。
|
12天前
|
缓存 自然语言处理 安全
快速调用 Deepseek API!【超详细教程】
Deepseek 强大的功能,在本教程中,将指导您如何获取 DeepSeek API 密钥,并演示如何使用该密钥调用 DeepSeek API 以进行调试。

热门文章

最新文章