找回密码
 立即注册
搜索
查看: 2919|回复: 14

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

[复制链接]
     
发表于 2018-3-21 14:00 | 显示全部楼层 |阅读模式
原网址:
https://pubg.op.gg/user/shroud?server=na

希望爬取的数据:


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

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

使用道具 举报

     
发表于 2018-3-21 14:09 | 显示全部楼层
用Chrome DevelopTool  定位到节点 右键Copy→ XPath 或者 Selector

评分

参与人数 1战斗力 +2 收起 理由
LonelyTB + 2 好评加鹅

查看全部评分

回复

使用道具 举报

     
 楼主| 发表于 2018-3-21 14:19 | 显示全部楼层
StarForceTi 发表于 2018-3-21 14:09
用Chrome DevelopTool  定位到节点 右键Copy→ XPath 或者 Selector

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

使用道具 举报

     
 楼主| 发表于 2018-3-21 19:21 | 显示全部楼层
本帖最后由 LonelyTB 于 2018-3-21 19:22 编辑
StarForceTi 发表于 2018-3-21 14:09
用Chrome DevelopTool  定位到节点 右键Copy→ XPath 或者 Selector

一个新的问题:
  1. val url = URL("https://pubg.op.gg/user/2-Iraka?server=as")
  2. val document = Jsoup.parse(url.readText())
  3. println(document.select("#userNickname"))
复制代码
使用以上代码,可以正确输出 #userNickname 处的内容,但是如果用以下 selector
  1. #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么?直接拿不就行了么
回复

使用道具 举报

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

恩,我知道 XPath 的话应该是
  1. //*[@id="rankedStatsWrap"]/div[2]/div[1]/div/div[3]/div/div/div/ul/li[6]/div[2]
复制代码


我程序是 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

评分

参与人数 1战斗力 +2 收起 理由
LonelyTB + 2 好评加鹅

查看全部评分

回复

使用道具 举报

     
 楼主| 发表于 2018-3-22 00:53 | 显示全部楼层
董卓 发表于 2018-3-21 20:12
不懂Kotlin,但单纯data-user_nickname是这个是很简单的么?
System.out.println(doc.getElementById("user ...

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

使用道具 举报

     
发表于 2018-3-22 09:19 来自手机 | 显示全部楼层
学习了
回复

使用道具 举报

     
 楼主| 发表于 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() 直接读出网页数据
不加过滤直接输出读取的数据
  1. val url = URL("https://pubg.op.gg/api/users/5a801429f0ffbe0001bb92b3/ranked-stats?season=2018-03&server=as&queue_size=4&mode=tpp")
  2. val html = url.readText()
  3. println(html)
复制代码


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

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




回复

使用道具 举报

     
发表于 2018-3-22 18:47 | 显示全部楼层
是不是你抓的太狠了,服务器返回假数据反爬
回复

使用道具 举报

     
 楼主| 发表于 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之类的信息
当然这要是写服务的人很有心了,很可能什么都没有也是十分正常的

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

使用道具 举报

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

感谢解惑。

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

其实我的需求并没有大到要爬它全站。我只需要在五分钟左右的时间爬取一局游戏100个人的数据就能接受了(正好是 5*60/100=3 秒的间隔)。
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

Archiver|手机版|小黑屋|上海互联网违法和不良信息举报中心|网上有害信息举报专区|962110 反电信诈骗|举报电话 021-62035905|Stage1st ( 沪ICP备13020230号-1|沪公网安备 31010702007642号 )

GMT+8, 2025-8-15 14:40 , Processed in 0.125278 second(s), 8 queries , Gzip On, Redis On.

Powered by Discuz! X3.5

© 2001-2025 Discuz! Team.

快速回复 返回顶部 返回列表