爬虫从入门到放弃——抓取前端渲染的页面_No_Game_No_Life_的博客-CSDN博客_js渲染的网页怎么爬虫


本站和网页 https://blog.csdn.net/No_Game_No_Life_/article/details/87713340 的作者无关,不对其内容负责。快照谨为网络故障时之索引,不代表被搜索网站的即时页面。

爬虫从入门到放弃——抓取前端渲染的页面_No_Game_No_Life_的博客-CSDN博客_js渲染的网页怎么爬虫
爬虫从入门到放弃——抓取前端渲染的页面
No_Game_No_Life_
于 2019-02-19 17:12:47 发布
8660
收藏
14
分类专栏:
爬虫
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/No_Game_No_Life_/article/details/87713340
版权
爬虫
专栏收录该内容
11 篇文章
1 订阅
订阅专栏
抓取前端渲染的页面
随着AJAX技术不断的普及,以及现在AngularJS这种Single-page application框架的出现,现在js渲染出的页面越来越多。对于爬虫来说,这种页面是比较讨厌的:仅仅提取HTML内容,往往无法拿到有效的信息。那么如何处理这种页面呢?总的来说有两种做法:
在抓取阶段,在爬虫中内置一个浏览器内核,执行js渲染页面后,再抓取。这方面对应的工具有Selenium、HtmlUnit或者PhantomJs。但是这些工具都存在一定的效率问题,同时也不是那么稳定。好处是编写规则同静态页面一样。因为js渲染页面的数据也是从后端拿到,而且基本上都是AJAX获取,所以分析AJAX请求,找到对应数据的请求,也是比较可行的做法。而且相对于页面样式,这种接口变化可能性更小。缺点就是找到这个请求,并进行模拟,是一个相对困难的过程,也需要相对多的分析经验。
对比两种方式,我的观点是,对于一次性或者小规模的需求,用第一种方式省时省力。但是对于长期性的、大规模的需求,还是第二种会更靠谱一些。对于一些站点,甚至还有一些js混淆的技术,这个时候,第一种的方式基本是万能的,而第二种就会很复杂了。
对于第一种方法,webmagic-selenium就是这样的一个尝试,它定义了一个Downloader,在下载页面时,就是用浏览器内核进行渲染。selenium的配置比较复杂,而且跟平台和版本有关,没有太稳定的方案。我们先来介绍这个尝试吧。
使用Selenium来抓取动态加载的页面
因为无论怎样动态加载,基础信息总归是包含在初始页面中得,所以我们可以用爬虫代码来模拟js代码,js读取页面元素值,我们也读取页面元素值;js发送ajax,我们就拼凑参数、发送ajax并解析返回的json。这样总归是能做的,但是比较麻烦,有没有比较省力的方法呢?比较好的方法大概是内嵌一个浏览器了。
Selenium是一个模拟浏览器,进行自动化测试的工具,它提供一组API可以与真实的浏览器内核交互。Selenium是跨语言的,有Java、C#、python等版本,并且支持多种浏览器,chrome、firefox以及IE都支持。
在Java项目中使用Selenium,需要做两件事:
在项目中引入Selenium的Java模块,以Maven为例:
<dependency>
<groupId>org.seleniumhq.selenium</groupId>
<artifactId>selenium-java</artifactId>
<version>2.33.0</version>
</dependency>
下载对应的driver,以chrome为例: http://code.google.com/p/chromedriver/downloads/list 下载后,需要将driver的位置写到Java的环境变量里,例如在mac下将其下载到了/Users/yihua/Downloads/chromedriver,则需要在程序里添加以下代码(当然在JVM参数里写-Dxxx=xxx也是可以的):
<!-- lang: java -->
System.getProperties().setProperty("webdriver.chrome.driver","/Users/yihua/Downloads/chromedriver");
Selenium的API挺简单的,核心是WebDriver,下面是动态渲染页面,并获取最终html的代码:
@Test
public void testSelenium() {
System.getProperties().setProperty("webdriver.chrome.driver", "/Users/yihua/Downloads/chromedriver");
WebDriver webDriver = new ChromeDriver();
webDriver.get("http://huaban.com/");
WebElement webElement = webDriver.findElement(By.xpath("/html"));
System.out.println(webElement.getAttribute("outerHTML"));
webDriver.close();
值得注意的是,每次new ChromeDriver(),Selenium都会建立一个Chrome进程,并使用一个随机端口在Java中与chrome进程进行通信来交互。由此可见有两个问题:
因此如果直接关闭Java程序,Chrome进程可能是无法关闭的。这里需要显示的调用webDriver.close()来关闭进程。创建进程的开销还是比较大的,尽量对webDriver进行复用会比较好。可惜根据官方的文档,webDriver不是线程安全的,所以我们需要建立一个webDriver池来保存它们。
最后说说效率问题。嵌入浏览器之后,不但要多花CPU去渲染页面,还要下载页面附加的资源。似乎单个webDriver中的静态资源是有缓存的,初始化之后,访问速度会加快。试用ChromeDriver加载了100次花瓣的首页(http://huaban.com/),共耗时263秒,平均每个页面2.6秒。
/**
* 花瓣网抽取器。<br>
* 使用Selenium做页面动态渲染。<br>
*/
public class HuabanProcessor implements PageProcessor {
private Site site;
@Override
public void process(Page page) {
page.addTargetRequests(page.getHtml().links().regex("http://huaban\\.com/.*").all());
if (page.getUrl().toString().contains("pins")) {
page.putField("img", page.getHtml().xpath("//div[@id='pin_img']/img/@src").toString());
} else {
page.getResultItems().setSkip(true);
@Override
public Site getSite() {
if (site == null) {
site = Site.me().setDomain("huaban.com").addStartUrl("http://huaban.com/").setSleepTime(1000);
return site;
public static void main(String[] args) {
Spider.create(new HuabanProcessor()).thread(5)
.scheduler(new RedisScheduler("localhost"))
.pipeline(new FilePipeline("/data/webmagic/test/"))
.downloader(new SeleniumDownloader("/Users/yihua/Downloads/chromedriver"))
.run();
public class SeleniumDownloader implements Downloader, Closeable {
private volatile WebDriverPool webDriverPool;
private Logger logger = Logger.getLogger(getClass());
private int sleepTime = 0;
private int poolSize = 1;
private static final String DRIVER_PHANTOMJS = "phantomjs";
/**
* 新建
* @param chromeDriverPath chromeDriverPath
*/
public SeleniumDownloader(String chromeDriverPath) {
System.getProperties().setProperty("webdriver.chrome.driver",
chromeDriverPath);
/**
* Constructor without any filed. Construct PhantomJS browser
* @author bob.li.0718@gmail.com
*/
public SeleniumDownloader() {
// System.setProperty("phantomjs.binary.path",
// "/Users/Bingo/Downloads/phantomjs-1.9.7-macosx/bin/phantomjs");
/**
* set sleep time to wait until load success
* @param sleepTime sleepTime
* @return this
*/
public SeleniumDownloader setSleepTime(int sleepTime) {
this.sleepTime = sleepTime;
return this;
@Override
public Page download(Request request, Task task) {
checkInit();
WebDriver webDriver;
try {
webDriver = webDriverPool.get();
} catch (InterruptedException e) {
logger.warn("interrupted", e);
return null;
logger.info("downloading page " + request.getUrl());
webDriver.get(request.getUrl());
try {
Thread.sleep(sleepTime);
} catch (InterruptedException e) {
e.printStackTrace();
WebDriver.Options manage = webDriver.manage();
Site site = task.getSite();
if (site.getCookies() != null) {
for (Map.Entry<String, String> cookieEntry : site.getCookies()
.entrySet()) {
Cookie cookie = new Cookie(cookieEntry.getKey(),
cookieEntry.getValue());
manage.addCookie(cookie);
/*
* TODO You can add mouse event or other processes
* @author: bob.li.0718@gmail.com
*/
WebElement webElement = webDriver.findElement(By.xpath("/html"));
String content = webElement.getAttribute("outerHTML");
Page page = new Page();
page.setRawText(content);
page.setHtml(new Html(content, request.getUrl()));
page.setUrl(new PlainText(request.getUrl()));
page.setRequest(request);
webDriverPool.returnToPool(webDriver);
return page;
private void checkInit() {
if (webDriverPool == null) {
synchronized (this) {
webDriverPool = new WebDriverPool(poolSize);
@Override
public void setThread(int thread) {
this.poolSize = thread;
@Override
public void close() throws IOException {
webDriverPool.closeAll();
分析AJAX请求
这里我们以AngularJS中文社区http://angularjs.cn/为例。
(1) 如何判断前端渲染
判断页面是否为js渲染的方式比较简单,在浏览器中直接查看源码(Windows下Ctrl+U,Mac下command+alt+u),如果找不到有效的信息(Ctrl+F),则基本可以肯定为js渲染。 这个例子中,在页面中的标题“有孚计算机网络-前端攻城师”在源码中无法找到,则可以断定是js渲染,并且这个数据是AJAX得到。
(2)分析请求
下面我们进入最难的一部分:找到这个数据请求。这一步能帮助我们的工具,主要是浏览器中查看网络请求的开发者工具。
以Chome为例,我们打开“开发者工具”(Windows下是F12,Mac下是command+alt+i),然后重新刷新页面(也有可能是下拉页面,总之是所有你认为可能触发新数据的操作),然后记得保留现场,把请求一个个拿来分析吧。
这一步需要一点耐心,但是也并不是无章可循。首先能帮助我们的是上方的分类筛选(All、Document等选项)。如果是正常的AJAX,在XHR标签下会显示,而JSONP请求会在Scripts标签下,这是两个比较常见的数据类型。
然后你可以根据数据大小来判断一下,一般结果体积较大的更有可能是返回数据的接口。剩下的,基本靠经验了,例如这里这个"latest?p=1&s=20"一看就很可疑… 对于可疑的地址,这时候可以看一下响应体是什么内容了。这里在开发者工具看不清楚,我们把URLhttp://angularjs.cn/api/article/latest?p=1&s=20复制到地址栏,重新请求一次。查看结果,看来我们找到了想要的。
有些时候,返回的类型不是json格式而是html格式的,这点我们之后再讲解。
(3) 编写程序
回想一下之前列表+目标页的例子,会发现我们这次的需求,跟之前是类似的,只不过换成了AJAX方式-AJAX方式的列表,AJAX方式的数据,而返回数据变成了JSON。那么,我们仍然可以用上次的方式,分为两种页面来进行编写:
1)数据列表: 在这个列表页,我们需要找到有效的信息,来帮助我们构建目标AJAX的URL。这里我们看到,这个_id应该就是我们想要的帖子的id,而帖子的详情请求,就是由一些固定URL加上这个id组成。所以在这一步,我们自己手动构造URL,并加入到待抓取队列中。这里我们使用JsonPath这种选择语言来选择数据(webmagic-extension包中提供了JsonPathSelector来支持它)。
if (page.getUrl().regex(LIST_URL).match()) {
//这里我们使用JSONPATH这种选择语言来选择数据
List<String> ids = new JsonPathSelector("$.data[*]._id").selectList(page.getRawText());
if (CollectionUtils.isNotEmpty(ids)) {
for (String id : ids) {
page.addTargetRequest("http://angularjs.cn/api/article/"+id);
2)目标数据 有了URL,实际上解析目标数据就非常简单了,因为JSON数据是完全结构化的,所以省去了我们分析页面,编写XPath的过程。这里我们依然使用JsonPath来获取标题和内容。
page.putField("title", new JsonPathSelector("$.data.title").select(page.getRawText()));
page.putField("content", new JsonPathSelector("$.data.content").select(page.getRawText()));
最后的代码如下:
public class AngularJSProcessor implements PageProcessor {
private Site site = Site.me();
private static final String ARITICALE_URL = "http://angularjs\\.cn/api/article/\\w+";
private static final String LIST_URL = "http://angularjs\\.cn/api/article/latest.*";
@Override
public void process(Page page) {
if (page.getUrl().regex(LIST_URL).match()) {
List<String> ids = new JsonPathSelector("$.data[*]._id").selectList(page.getRawText());
if (CollectionUtils.isNotEmpty(ids)) {
for (String id : ids) {
page.addTargetRequest("http://angularjs.cn/api/article/" + id);
} else {
page.putField("title", new JsonPathSelector("$.data.title").select(page.getRawText()));
page.putField("content", new JsonPathSelector("$.data.content").select(page.getRawText()));
@Override
public Site getSite() {
return site;
public static void main(String[] args) {
Spider.create(new AngularJSProcessor()).addUrl("http://angularjs.cn/api/article/latest?p=1&s=20").run();
在这个例子中,我们分析了一个比较经典的动态页面的抓取过程。实际上,动态页面抓取,最大的区别在于:它提高了链接发现的难度。我们对比一下两种开发模式:
后端渲染的页面
下载辅助页面=>发现链接=>下载并分析目标HTML
前端渲染的页面
发现辅助数据=>构造链接=>下载并分析目标AJAX
对于不同的站点,这个辅助数据可能是在页面HTML中已经预先输出,也可能是通过AJAX去请求,甚至可能是多次数据请求的过程,但是这个模式基本是固定的。
但是这些数据请求的分析比起页面分析来说,仍然是要复杂得多,所以这其实是动态页面抓取的难点。所以,之前说的,如果js请求的结果也是Html,其实就只需要再构造一个http请求而已,把要请求的Url加入待查询的Url即可。
所以对于之前的例子,公告获取不到,怎么办呢? 查看源码后是这样的: 断言:是通过ajax来获取的。 然后我们查看请求的Url: 返回的是html啊,很简单,把请求的链接放进去,再Process一遍不就好了么?我们来看看url是啥: https://www.cnblogs.com/mvc/blog/BlogPostInfo.aspx?blogId=368840&postId=10401378&blogApp=hiram-zhang&blogUserGuid=79b817bc-bd91-4e5c-363f-08d49c352df3&_=1550567127429 触到了知识的盲区。 我???????? 这id哪来的?? 我???? 我现在是风沙迷了眼了。。。。 算了等我弄明白再来搞吧,难搞哦。
public void process(Page page) {
//判断链接是否符合http://www.cnblogs.com/任意个数字字母-/p/7个数字.html格式
page.putField("name",page.getHtml().xpath("//*[@id=\"author_profile_detail\"]/a[1]/text()"));
public static void main(String[] args) {
long startTime, endTime;
System.out.println("开始爬取...");
startTime = System.currentTimeMillis();
Spider.create(new MyProcessor2()).addUrl("https://www.cnblogs.com/mvc/blog/BlogPostInfo.aspx?blogId=368840&postId=10401378&blogApp=hiram-zhang&blogUserGuid=79b817bc-bd91-4e5c-363f-08d49c352df3&_=1550567127429").addPipeline(new MyPipeline()).thread(5).run();
endTime = System.currentTimeMillis();
System.out.println("爬取结束,耗时约" + ((endTime - startTime) / 1000) + "秒,抓取了"+count+"条记录");
No_Game_No_Life_
关注
关注
点赞
14
收藏
打赏
评论
爬虫从入门到放弃——抓取前端渲染的页面
抓取前端渲染的页面随着AJAX技术不断的普及,以及现在AngularJS这种Single-page application框架的出现,现在js渲染出的页面越来越多。对于爬虫来说,这种页面是比较讨厌的:仅仅提取HTML内容,往往无法拿到有效的信息。那么如何处理这种页面呢?总的来说有两种做法:在抓取阶段,在爬虫中内置一个浏览器内核,执行js渲染页面后,再抓取。这方面对应的工具有Selenium、...
复制链接
扫一扫
专栏目录
单页扒站小工具-扒出来的页面非常完美
09-26
网站源码下载,可直接将源码按css、images、js目录下载,不产生多余的代码
参与评论
您还未登录,请先
登录
后发表或查看评论
Python爬虫原理
最新发布
m0_72557783的博客
11-27
23
简单来说互联网是由一个个站点和网络设备组成的大网,我们通过浏览器访问站点,站点把HTML、JS、CSS代码返回给浏览器,这些代码经过浏览器解析、渲染,将丰富多彩的网页呈现我们眼前
Python爬虫基础(三) —— 爬取动态渲染页面
hellocoding的博客
03-17
4566
文章目录使用Selenium库例子引入声明游览器对象访问页面查找节点单个节点多个节点节点交互动作链模拟执行javascript获取节点信息获取属性获取文本值获取id,位置,标签名和大小切换Frame延时等待隐式等待显示等待前进和后退Cookies选项卡管理
  虽然有些通过ajax动态渲染出来的页面通过对请求链接的分析我们仍然可以使用urllib或requests库来进行数据爬取,但javascr...
使用Nodejs爬取网页某个数据并把爬到的数据写入excel (前端部分,服务端部分看下一篇)
qq_45104282的博客
11-03
97
使用Nodejs爬取网页某个数据并把爬到的数据写入excel 母胎级教学
对于爬虫遇到的JS渲染的问题的一些解决方法
weixin_40995588的博客
12-05
1841
对于爬虫遇到的一些关于JS渲染的解决方法:
1.动用动态技术
Selenium+PhantomJS或者其他的一些动态框架
2.Splash:
我没用过,所以不好评价
3.自己动用Google Chrome找寻请求,然后找出参数进行模拟.
比较好练手的就是http://www.dm5.com
推荐去看下这个....
动用动态技术的...
爬虫--js渲染的网页的基本解决方法(提供思路)
qq_41562377的博客
05-11
2281
https://blog.csdn.net/qq_40925239/article/details/89453291
爬虫怎样解决JavaScript渲染问题
春天的波菜
06-05
1361
Python爬虫怎么处理js动态渲染的网页?
欧阳金城-武
07-29
7333
可以先看看集搜客gooseeker开源爬虫项目
里面使用了一个 Selenium库的东西,可以调用浏览器渲染页面,然后处理渲染后的页面
Selenium与PhantomJS搭配采集动态网页内容是比较经典的方案,PhantomJS可以在后台实现页面的渲染,而不是调用浏览器完成...
爬虫日记之05两种Web请求渲染过程(附图解和网站实例)
zty5556666的博客
03-05
996
两种web请求渲染过程,内附图解和网站实例
爬虫-动态JavaScript渲染
xiaoyaojun666的博客
11-08
1251
爬虫-动态JavaScript渲染
问题
能够成功爬取
但是爬取下来的html文件永远都是一个“JavaScript不启用系统不能运行”的页面
<body>
<noscript><strong>We're sorry but system doesn't work properly without JavaScript enabled. Please enable it to
continue.</strong> <strong&gt
python-爬虫-js-渲染-html
qq_43615820的博客
04-16
485
python 渲染 js 代码
简单、便捷
from requests_html import HTMLSession
session = HTMLSession()
r = session.get('http://127.0.0.1:10006?key=admin')
r.html.render()
print(r.html.html)
爬虫爬取js渲染网站的思路
笑笑布丁的博客
11-20
2043
如何辨别哪些网站时js渲染的网站?
使用requests库访问得到网页文本是否与浏览器得到的网页文本一致,若不一致(可能会存在因浏览器不同,导致不同结构的细微差异),网站多多少少存在js渲染的情况。
如何爬取js渲染的网站呢?
在这里我提供三种思路:
1、使用 selenium pyppeteer splash等等模拟浏览器的方式去爬取,这是最便捷的方法,缺点就是耗时比较慢,但胜在渲染无敌,xpa...
爬虫笔记1——服务器渲染和客户端渲染
daige123的博客
07-13
860
服务器渲染和客户端渲染
不止是动态渲染反爬虫
成都_杨洋
04-03
1226
动态网页比静态网页更具有交互性,能给用户提供更好的体验。动态网页中常见的表现形式有下拉刷新,点击切换和悬停显示等。有Javascript改变HTML_DOM导致页面内容发生变化的现象称为动态渲染。很多时候开发者只是想完成某个交互功能,而不是特意区分正常用户的爬虫程序,但这在不经意间限制了爬虫对数据的获取。由于编程语言没有像浏览器一样内置Javascript解释器和渲染引擎,所以动态渲染是天然的反爬...
那些年,我爬过的北科(六)——反反爬虫之js渲染
weixin_34061555的博客
12-08
252
反爬虫和反反爬虫?
从本章开始,我们将要进入反反爬虫篇的内容。
感觉如果是第一听到这个名字的读者肯定是懵逼的状态。现在我们先来介绍一下什么是爬虫、反爬虫、反反爬虫。
爬虫其实就是我们前面所学的代码,直接使用requests.get("http://xxx.com")就能拿到网站的源码。
但是很多时候,我们获取的都是有价值的数据,而网站开发者就不想让我们拿到他们的数据,就有了很多反爬虫的策略,不让我...
python 爬虫js渲染_小白学 Python 爬虫(39): JavaScript 渲染服务 scrapy-splash 入门...
weixin_39684995的博客
12-15
182
人生苦短,我用 Python前文传送门:引言Splash 是一种 JavaScript 渲染服务,是一个带有 HTTP API 的轻量级浏览器,同时它对接了 Python3 中的 Twisted 和 QT 库。通过它,我们同样可以实现动态渲染页面的抓取。功能说明:并行处理多个网页;获取 HTML 结果和/或获取屏幕截图;关闭图片或使用 Adblock Plus 规则来加快渲染速度;在页面上下文中执...
爬虫-动态渲染页面爬取-Selenium
weixin_43825323的博客
04-29
101
文章目录1-Selenium介绍2-准备工作3-基本使用4-声明浏览器对象5-访问页面browser.get(url)6-查找节点7-节点交互8-动作链9-执行JavaScript—execute_script()10-获取节点信息11-切换Frame—switch_to.frame()12-延时等待12-1-隐式等待—implicitly_wait()12-2-显式等待—WebDriverWait()12-3-等待条件及其含义13-前进forward()、后退back()14-Cookies15-选项卡管
“相关推荐”对你有帮助么?
非常没帮助
没帮助
一般
有帮助
非常有帮助
提交
©️2022 CSDN
皮肤主题:数字20
设计师:CSDN官方博客
返回首页
No_Game_No_Life_
CSDN认证博客专家
CSDN认证企业博客
码龄7年
暂无认证
531
原创
7821
周排名
97万+
总排名
41万+
访问
等级
9674
积分
5175
粉丝
500
获赞
134
评论
949
收藏
私信
关注
热门文章
Java设计模式——命令模式
20516
爬虫从入门到放弃——抓取前端渲染的页面
8659
(休息几天)读曼昆之微观经济学——税收归宿
8511
面试总结:Golang常见面试题汇总
5960
均值方差分析与风险资产组合
5701
分类专栏
面试准备
42篇
并发编程
16篇
Go
6篇
Golang架构直通车
9篇
LeetCode
56篇
Java架构直通车
59篇
数据库
22篇
Fabric
12篇
Kafka
6篇
Fabric实践
12篇
BlockChain
7篇
K8s
8篇
Docker
5篇
SpringBoot
7篇
微服务
16篇
java
48篇
经济学
25篇
分布式架构
47篇
java设计之道
34篇
搜索引擎
8篇
读书笔记
15篇
爬虫
11篇
大数据基础
16篇
一拳超人从不秃头
16篇
最新评论
面试准备:MySQL建立索引的原则
weixin_42159986:
!=
not in
null
返回结果远远小于全表扫描的情况下是走索引的
K8s从零开始搭建Fabric网络
songcihaha:
大哥请问你是用的ubuntu吗?系统是x64的吗?
K8s从零开始搭建Fabric网络
songcihaha:
我想用这个k8s搭建网络来着,想问问
K8s从零开始搭建Fabric网络
songcihaha:
大哥你好厉害呦,我想向您咨询点相关的问题,你有空回我一下吗,小妹万分荣幸,保佑大哥能看到我的留言回复我
面试准备:MySQL建立索引的原则
No_Game_No_Life_:
varchar 类型 + 前缀索引 吧
您愿意向朋友推荐“博客详情页”吗?
强烈不推荐
不推荐
一般般
推荐
强烈推荐
提交
最新文章
Leetcode 995. K 连续位的最小翻转次数——差分数组
面试准备:海量数据的处理方式
Java BufferedInputStream原理及设计模式分析
2021年24篇
2020年167篇
2019年216篇
2018年123篇
2017年5篇
2016年23篇
目录
目录
分类专栏
面试准备
42篇
并发编程
16篇
Go
6篇
Golang架构直通车
9篇
LeetCode
56篇
Java架构直通车
59篇
数据库
22篇
Fabric
12篇
Kafka
6篇
Fabric实践
12篇
BlockChain
7篇
K8s
8篇
Docker
5篇
SpringBoot
7篇
微服务
16篇
java
48篇
经济学
25篇
分布式架构
47篇
java设计之道
34篇
搜索引擎
8篇
读书笔记
15篇
爬虫
11篇
大数据基础
16篇
一拳超人从不秃头
16篇
目录
评论
被折叠的 条评论
为什么被折叠?
到【灌水乐园】发言
查看更多评论
打赏作者
No_Game_No_Life_
你的鼓励将是我创作的最大动力
¥2
¥4
¥6
¥10
¥20
输入1-500的整数
余额支付
(余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付
您的余额不足,请更换扫码支付或充值
打赏作者
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额
抵扣说明:
1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。 2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。
余额充值