LonelyTB 发表于 2018-3-21 14:00

业余程序员,请教下一个简单的网页爬取怎么写。

原网址:
https://pubg.op.gg/user/shroud?server=na

希望爬取的数据:
http://ww1.sinaimg.cn/large/006NGikrgy1fpke7hyfszj30pb0jgwlg.jpg

使用的是 jsoup 来爬取。虽然 jsoup 里面有选择器,但是面对这种大量相同 class name 的情况不知道怎么处理。

请教一下解决思路是怎样的?多谢。

StarForceTi 发表于 2018-3-21 14:09

用Chrome DevelopTool定位到节点 右键Copy→ XPath 或者 Selector

LonelyTB 发表于 2018-3-21 14:19

StarForceTi 发表于 2018-3-21 14:09
用Chrome DevelopTool定位到节点 右键Copy→ XPath 或者 Selector

明白了,原来是这么玩的。多谢多谢。

LonelyTB 发表于 2018-3-21 19:21

本帖最后由 LonelyTB 于 2018-3-21 19:22 编辑

StarForceTi 发表于 2018-3-21 14:09
用Chrome DevelopTool定位到节点 右键Copy→ XPath 或者 Selector
一个新的问题:
val url = URL("https://pubg.op.gg/user/2-Iraka?server=as")
val document = Jsoup.parse(url.readText())
println(document.select("#userNickname"))使用以上代码,可以正确输出 #userNickname 处的内容,但是如果用以下 selector
#rankedStatsWrap > div.ranked-stats-wrapper__list > div:nth-child(1) > div > div:nth-child(3) > div > div > div > ul > li:nth-child(6) > div.ranked-stats__value
则输出不正确。
请问是这里的 selector 格式不正确吗?(我是 Chrome Developer Tools 上直接复制下来的。)还是别的什么地方有问题?

绕指流光 发表于 2018-3-21 19:46

本帖最后由 绕指流光 于 2018-3-21 19:50 编辑

当然不正确,XPATH的语法不是这样的,请先搜一下XPATH相关的知识,再装一个叫XPATH HELPER的插件,方便极了。
另外既然class的名称唯一,直接用@就好了
例如:
DocumentNode.SelectNodes("//div[@class='mainArea']/ul/li");

不过点开那个页面瞅了一眼,妥妥的动态页面,你按这个路数应该是爬不到的
如果有时间研究可以搜一下SCRAPY这样成熟的爬虫框架,省很多功夫,但是动态页面的数据仍然要其他lib辅助才行

董卓 发表于 2018-3-21 19:49

document.getElementById('userNickname').attributes['data-user_nickname'].value
不是有id么?直接拿不就行了么

LonelyTB 发表于 2018-3-21 19:55

绕指流光 发表于 2018-3-21 19:46
当然不正确,XPATH的语法不是这样的,请先搜一下XPATH相关的知识,再装一个叫XPATH HELPER的插件,方便极了 ...

恩,我知道 XPath 的话应该是 //*[@id="rankedStatsWrap"]/div/div/div/div/div/div/div/ul/li/div

我程序是 Kotlin 语言,在里面用的 Jsoup。但是 Jsoup 貌似不支持 XPath,所以我用的 Selector 路径写。
然后写出来发现一层路径 #userNickname 的话没问题,但是多层的话输出不正确。

我再自己研究下吧,貌似有个 JsoupXpath 可以用。

董卓 发表于 2018-3-21 20:12

本帖最后由 董卓 于 2018-3-21 20:51 编辑

不懂Kotlin,但单纯data-user_nickname是这个是很简单的么?
System.out.println(doc.getElementById("userNickname").attr("data-user_nickname"));


而ranked-stats下的内容,在静态document里面只被加载了部分,你要的部分是通过动态加载的,所以拿不到吧。
你可以断点看一下doc的toString确认。


doc加载到div data-selector="game-point"的只有一行
而动态代码之后的页面是有好长好长的代码


无聊我再看看哪个js脚本进来的
opgg-pubg-static.akamaized.net/js/app.playerInfo.js?id=18d810d3b5d6cc53ce67
这个里面加载进来的
进一步的api是这个
pubg.op.gg/api/users/59fe237a6e6f210001da80f9/ranked-stats?season=2018-03&server=na&queue_size=2&mode=fpp
"api/users/{user_id}/ranked-stats"
paramNames: ["season", "server", "queue_size", "mode"],

id在div id="userNickname" data-user_nickname="shroud" data-user_id="59fe237a6e6f210001da80f9"
                         class="player-summary__name "
一开始就有

然后狗一狗,这api已经被研究透了啊:
https://www.npmjs.com/package/pubg.op.gg
https://github.com/PatyYe/pubg.op.gg-API-Wrapper

LonelyTB 发表于 2018-3-22 00:53

董卓 发表于 2018-3-21 20:12
不懂Kotlin,但单纯data-user_nickname是这个是很简单的么?
System.out.println(doc.getElementById("user ...

非常感谢,已经成功抓到数据了。

aiboers110 发表于 2018-3-22 09:19

学习了

LonelyTB 发表于 2018-3-22 18:20

本帖最后由 LonelyTB 于 2018-3-22 21:14 编辑

董卓 发表于 2018-3-21 20:12
不懂Kotlin,但单纯data-user_nickname是这个是很简单的么?
System.out.println(doc.getElementById("user ...
发现一个很诡异的问题:通过 API 获取的数据完全是随机的。
为了查错:
我把链接写死(即 user_id 什么的都是固定的)
通过 Kotlin 的 readText() 直接读出网页数据
不加过滤直接输出读取的数据
val url = URL("https://pubg.op.gg/api/users/5a801429f0ffbe0001bb92b3/ranked-stats?season=2018-03&server=as&queue_size=4&mode=tpp")
val html = url.readText()
println(html)

这样运行,结果每次输出的值都不一样(而且在输出的值中发现,甚至有 死亡次数>游戏场次 这种不合逻辑的情况)。

这个问题有什么合理的解释吗?




EraserKing 发表于 2018-3-22 18:47

是不是你抓的太狠了,服务器返回假数据反爬

LonelyTB 发表于 2018-3-22 21:12

本帖最后由 LonelyTB 于 2018-3-22 23:05 编辑

EraserKing 发表于 2018-3-22 18:47
是不是你抓的太狠了,服务器返回假数据反爬
那可能是了,我的每次抓取间隔是三秒,本来以为已经够长了。
这种情况下怎么才能恢复获取正确数据呢?另外怎么可以测试出来网站对爬取数据的相关限制?

EDIT:
发现我自己的并发写的有点问题,实际并不是三秒间隔,我修改了再看看。

董卓 发表于 2018-3-23 20:42

LonelyTB 发表于 2018-3-22 21:12
那可能是了,我的每次抓取间隔是三秒,本来以为已经够长了。
这种情况下怎么才能恢复获取正确数据呢?另外 ...

如果是webservice友好的服务,那么有的会给你点30x乃至40x的提示
如果是200,看看http的返回头(头,非体)里面,有没有友好访问的要求、quota之类的信息
当然这要是写服务的人很有心了,很可能什么都没有也是十分正常的

如果对这里网站要爬……
此处省略几千字,简单点说结论吧,订一个/买一个代理列表

LonelyTB 发表于 2018-3-25 03:54

董卓 发表于 2018-3-23 20:42
如果是webservice友好的服务,那么有的会给你点30x乃至40x的提示
如果是200,看看http的返回头(头,非体 ...

感谢解惑。

我今天试了下,爬取间隔三秒钟,已经能正常获取数据了,之前应该是被返回了虚假值。

其实我的需求并没有大到要爬它全站。我只需要在五分钟左右的时间爬取一局游戏100个人的数据就能接受了(正好是 5*60/100=3 秒的间隔)。
页: [1]
查看完整版本: 业余程序员,请教下一个简单的网页爬取怎么写。