写点什么

极复杂编码,下载《原神》角色高清图、中日无损配音,爬虫 16 / 120 例

发布于: 刚刚

各位 爬虫爱好者,今天咱们要采集的目标站点是 原神官网,核心目标数据为原神角色图,角色大头贴,角色昵称,角色配音文件。


待爬取页面分析

本次爬取的页面为:https://ys.mihoyo.com/main/character/mondstadt,其中 mondstadt 可以替换为 liyueinazuma


目标页面的列表页数据呈现如下图所示,数据量级不是很大。



进一步提取目标数据:


  1. 所有的数据都在 li 标签中;

  2. 其中 li 数据分为两部分,大图与音频在一个 li 标签中,大头贴与角色昵称在第二个 li 标签中,具体 DOM 结构如下所示:

  3. 最重要的音频下载地址如下图所示:

  4. 但是在实际抓取的过程中,却发现服务器返回的代码,并非开发者工具加载的样式,代码返回到前端,由前端 JS 进行了二次渲染。


具体你可以通过谷歌浏览器,打开如下链接查阅:


view-source:https://ys.mihoyo.com/main/character/mondstadt?char=0
复制代码

技术细节强调

本爬虫整体实现难度不大,有一些细节需要额外进行处理。


  1. 数据需要通过正则表达式进行提取,因服务器直接将其返回在页面中;

  2. 爬虫采集过程中需要进行音频下载;

  3. 动态传递参数到 Python 文件中。


其中数据合并使用 zip 函数实现,音频下载处理过程注意请求头与响应体即可,动态参数,采用 python 编译传参方式实现。

目标数据格式规定

在正式编码前,需要对目标数据存储格式进行二次确定,本次抓取输出的目录格式如下:


- download  - 琴    - xxx.mp3    - cover1.png    - xxxx.mp3  - 安柏    - xxx.mp3    - cover1.png    - xxxx.mp3
复制代码

编码时间

通过 requests 模块去获取服务器响应数据之后,发现上述分析还是出了一点点问题,下图是服务器响应的代码段,并没有开发者工具中捕获到的 DOM 结构。



通过 Visual studio Code 进行代码格式化发现,数据是直接渲染到页面上,再通过 JS 进行加载。重要部分数据如下所示:


charList: [                {                  title: "琴",                  icon: "https:\u002F\u002Fuploadstatic.mihoyo.com\u002Fcontentweb\u002F20200306\u002F2020030616591036729.png",                  cover1:                    "https:\u002F\u002Fuploadstatic.mihoyo.com\u002Fcontentweb\u002F20200729\u002F2020072917270791000.png",                  cover2:                    "https:\u002F\u002Fuploadstatic.mihoyo.com\u002Fcontentweb\u002F20200729\u002F2020072917273354266.png",                  name: "https:\u002F\u002Fuploadstatic.mihoyo.com\u002Fcontentweb\u002F20190926\u002F2019092620142281505.png",                  attr: "https:\u002F\u002Fuploadstatic.mihoyo.com\u002Fcontentweb\u002F20190926\u002F2019092620142687125.png",                  intro:                    "\u003Cp\u003E身为西风骑士团的代理团长,琴一直忠于职守,为人们带来安宁。虽然并非天赋异禀,但通过刻苦训练,如今的她已然能够独当一面。\u003Cbr \u002F\u003E\n当风魔龙的威胁开始临近,这位可靠的代理团长早已做好了准备,誓要守护蒙德。\u003C\u002Fp\u003E\n",                  sen: "https:\u002F\u002Fuploadstatic.mihoyo.com\u002Fcontentweb\u002F20190926\u002F2019092620144979413.png",                  lang: [a, b],                  cv: [                    {                      name: "林簌",                      audio: [                        "https:\u002F\u002Fwebstatic.mihayo.com\u002Fupload\u002Fop-public\u002F2019\u002F12\u002F11\u002F209a68a166b14b27e11a8b64c466ea7c_7021182076965695539.mp3",                        "https:\u002F\u002Fwebstatic.mihayo.com\u002Fupload\u002Fop-public\u002F2019\u002F12\u002F11\u002F806fad7c524efcebd55abc2ce4f8ce6a_5745385847854898057.mp3",                        "https:\u002F\u002Fwebstatic.mihayo.com\u002Fupload\u002Fop-public\u002F2019\u002F12\u002F11\u002F74c81976dc6f3868ecc264bbd143e571_4077467239236738470.mp3",                      ],                    },                    {                      name: "斋藤千和",                      audio: [                        "https:\u002F\u002Fuploadstatic.mihoyo.com\u002Fcontentweb\u002F20190926\u002F2019092620145220378.mp3",                        "https:\u002F\u002Fuploadstatic.mihoyo.com\u002Fcontentweb\u002F20190926\u002F2019092620145562610.mp3",                        "https:\u002F\u002Fuploadstatic.mihoyo.com\u002Fcontentweb\u002F20190926\u002F2019092620145849323.mp3",                      ],                    },                  ],                },
复制代码


此时再通过 lxml 进行提取就变得相对困难,但是使用正则表达式进行提取就变得简单很多。


本部分代码可使用 JSON 格式化校验工具,进行排版测试,也便于提取相关字段。下图忽略相关错误。



注意到上述代码中,还存在特殊字符 \u002F\u 的编码字串可以使用如下代码进行编译。


print("https:\\u002F\\u002Fuploadstatic.mihoyo.com\\u002Fcontentweb\\u002F20210508\\u002F2021050818254152089.png".encode('utf-8').decode("unicode-escape"))
复制代码


本案例比较麻烦的地方就是提取数据,由于整体难度不大,属于编码熟练度练习,故直接放出源码,下述代码在学习时,一定要注意各个编码细节。


# https://ys.mihoyo.com/main/character/mondstadtimport requestsimport sysimport randomimport reimport sysimport os

def get_headers(): uas = [ "Mozilla/5.0 (compatible; Baiduspider/2.0; +http://www.baidu.com/search/spider.html)", "Mozilla/5.0 (compatible; Baiduspider-render/2.0; +http://www.baidu.com/search/spider.html)", "Baiduspider-image+(+http://www.baidu.com/search/spider.htm)", "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.81 YisouSpider/5.0 Safari/537.36", "Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)", "Mozilla/5.0 (compatible; Googlebot-Image/1.0; +http://www.google.com/bot.html)", "Sogou web spider/4.0(+http://www.sogou.com/docs/help/webmasters.htm#07)", "Sogou News Spider/4.0(+http://www.sogou.com/docs/help/webmasters.htm#07)", "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0);", "Mozilla/5.0 (compatible; bingbot/2.0; +http://www.bing.com/bingbot.htm)", "Sosospider+(+http://help.soso.com/webspider.htm)", "Mozilla/5.0 (compatible; Yahoo! Slurp China; http://misc.yahoo.com.cn/help.html)" ] ua = random.choice(uas) headers = { "user-agent": ua, "referer": "https://www.baidu.com" } return headers

# 数据提取函数def format_text(text): # element = etree.HTML(text) # print(text) # all_li = element.cssselect("li.swiper-slide") # print(all_li) div_pattern = re.compile('charList:(\[.*?),mod3Index') match = div_pattern.search(text) div_html = match.group(1) title_pattern = re.compile('title:"(.*?)"') cover1_pattern = re.compile('cover1:"(.*?)"') cover2_pattern = re.compile('cover2:"(.*?)"') icon_pattern = re.compile('icon:"(.*?)"') # 该正则表达式比较复杂,需要重点理解 cv_pattern = re.compile( 'cv:\[\{name:"(?P<cn_name>.*?)"\,audio:\[(?P<cn_audios>.*?)\]\}\,\{name:"(?P<jp_name>.*?)"\,audio:\[(?P<jp_audios>.*?)\]\}\]\}') titles = title_pattern.findall(div_html) cover1s = cover1_pattern.findall(div_html) cover2s = cover2_pattern.findall(div_html) icons = icon_pattern.findall(div_html) cvs = cv_pattern.findall(div_html) print(cvs) # print(titles,cover1s,cover2s,icons,cvs) for index in range(0, len(titles)): my_dict = { "title": titles[index], "cover1": cover1s[index], "cover2": cover2s[index], "icon": icons[index], "cn_name": cvs[index][0], # 中文配音名称 "jp_name": cvs[index][2], # 日文配音名称 "cn_audios": cvs[index][1].split(","), "jp_audios": cvs[index][3].split(","), } save(my_dict) # print("https:\\u002F\\u002Fuploadstatic.mihoyo.com\\u002Fcontentweb\\u002F20210508\\u002F2021050818254152089.png".encode('utf-8').decode("unicode-escape")) # print(len(titles),len(cover1s),len(cover2s))

# 创建文件夹def save(my_dict): is_exists = os.path.exists("./download")
# 判断结果 if not is_exists: os.mkdir("./download")
# 提取数据
title = my_dict["title"] cover1 = my_dict["cover1"] cover2 = my_dict["cover2"] icon = my_dict["icon"] cn_name = my_dict["cn_name"] jp_name = my_dict["jp_name"] cn_audios = my_dict["cn_audios"] jp_audios = my_dict["jp_audios"]
# 创建目录 os.mkdir(f"./download/{title}") # 保存封面图1 save_img(cover1, title, "cover1")

def save_img(url, title, img_name): # 去除 \u 字符 url = url.encode('utf-8').decode("unicode-escape") try: res = requests.get(url, headers=get_headers(), timeout=5) with open(f'./download/{title}/{img_name}.png', "wb") as f: f.write(res.content) except Exception as e: print(e)

def run(url): try: res = requests.get(url, headers=get_headers(), timeout=5) format_text(res.text) except Exception as e: print("请求数据发生异常", e)

if __name__ == "__main__": argvs = sys.argv # 获取传递进来的参数 category = argvs[1] url = "https://ys.mihoyo.com/main/character/{}?char=0".format(category) print(url) run(url)
复制代码


执行上述代码,会得到部分高清图片,具体如下,每个文件夹都包含各个高清透明素材图。



再次修改代码,进行大头贴与全身图提取,得到如下数据。



对于 MP3 文件的下载,实现方式基本与图片一致,代码如下:


# 下载音频def save_audio(title, cn_name, cn_audios):    try:        for index in range(0, len(cn_audios)):            # 去除 \u 字符            url = cn_audios[index].encode('utf-8').decode("unicode-escape")            # 去除 url 左右双引号            url = url.strip('"')
res = requests.get(url, headers=get_headers(), timeout=5) with open(f'./download/{title}/{cn_name}_{index}.mp3', "wb") as f: f.write(res.content) except Exception as e: print(e)
复制代码


在处理音频地址时,需要注意由于提取的原因,字符串左右多了一个 ",需要手动去除,即上述代码中 url = url.strip('"') 部分内容。


调用 save_audio 代码,实现对中日配音的下载。


运行代码,将 蒙德城璃月港稻妻城 相关数据进行下载,测试过程发现正则表达式匹配存在一些问题,修改如下:


cv:\[\{name:[\"]?(?P<cn_name>.*?)[\"]?\,audio:\[(?P<cn_audios>.*?)\]\}\,\{name:[\"]?(?P<jp_name>.*?)[\"]?\,audio:\[(?P<jp_audios>.*?)\]\}\]\}
复制代码

收藏时间

来都来了,不发个评论,点个赞,收个藏吗

发布于: 刚刚阅读数: 2
用户头像

爬虫 100 例作者,蓝桥签约作者,博客专家 2021.02.06 加入

6 年产品经理+教学经验,3 年互联网项目管理经验; 互联网资深爱好者; 沉迷各种技术无法自拔,导致年龄被困在 25 岁; CSDN 爬虫 100 例作者。 个人公众号“梦想橡皮擦”。

评论

发布
暂无评论
极复杂编码,下载《原神》角色高清图、中日无损配音,爬虫 16 / 120 例