Steam crawler/scraper Python脚本未生成所需的所有信息?
我目前正在使用一个Steam crawler()脚本,该脚本基于带有游戏ID的数据集,能够抓取Steam评论数据(日期、评论文本、用户ID等)。我不是HTML抓取方面的专家,但根据我从代码(如下所示)中了解到的情况,脚本正在循环给定的游戏ID以收集所有评论,直到遇到这种特定情况Steam crawler/scraper Python脚本未生成所需的所有信息?,python,html,web-scraping,web-crawler,steam,Python,Html,Web Scraping,Web Crawler,Steam,我目前正在使用一个Steam crawler()脚本,该脚本基于带有游戏ID的数据集,能够抓取Steam评论数据(日期、评论文本、用户ID等)。我不是HTML抓取方面的专家,但根据我从代码(如下所示)中了解到的情况,脚本正在循环给定的游戏ID以收集所有评论,直到遇到这种特定情况endre=re.compile(r'({“success”:2})|(无更多评论) import argparse 导入csv 导入操作系统 进口稀土 导入套接字 导入字符串 导入URL库 导入urllib.reques
endre=re.compile(r'({“success”:2})|(无更多评论)
import argparse
导入csv
导入操作系统
进口稀土
导入套接字
导入字符串
导入URL库
导入urllib.request
导入urllib.parse
导入json
从上下文库导入关闭
从时间上导入睡眠
def下载页面(url、maxretries、超时、暂停):
尝试=0
htmlpage=None
而Trys=maxError:
打印('最大错误!')
打破
其他:
将open(os.path.join(gamedir,'reviews-%s.html'%page','w',encoding='utf-8')作为f:
htmlpage=htmlpage.decode()
如果endre.search(htmlpage):
打破
f、 写入(htmlpage)
第页=第+1页
解析的_json=(json.loads(htmlpage))
cursor=urllib.parse.quote(已解析的_json['cursor'])
将open(donefilename,'w',encoding='utf-8')作为f:
通过
def main():
parser=argparse.ArgumentParser(description='Strawler of Steam reviews')
parser.add_参数('-f','-force',help='force download,即使已成功下载,'必需=False,
action='store\u true')
parser.add_参数(
'-t','-timeout',help='http连接的超时时间(秒)。默认值:180',
必需=False,类型=int,默认值=180)
parser.add_参数(
'-r','-maxretries',help='Max retries下载文件。默认值:5',
必需=False,类型=int,默认值=3)
parser.add_参数(
'-p','-pause',help='http请求之间等待的秒数。默认值:0.5',required=False,默认值=0.01,
类型=浮动)
parser.add_参数(
'-m','-maxreviews',help='每个项目可下载的最大评论数。默认值:unlimited',required=False,
类型=整数,默认值=5000000)
parser.add_参数(
'-o'、'-out',help='Output base path',required=False,default='data')
parser.add_参数(
'-i'、'-id',help='File with game id',required=False,default='./data/games.csv')
args=parser.parse_args()
如果操作系统路径不存在(args.out):
os.makedirs(args.out)
ids=GetGameID(args.ids)
打印(“%s”游戏“%len(id))
getgamereviews(ID、args.timeout、args.maxretries、args.pause、args.out)
如果uuuu name uuuuuu='\uuuuuuu main\uuuuuuu':
main()
我目前面临的问题是,脚本没有正确地提取评论:例如,对于像《反恐全球攻势》这样拥有约1000000条评论的游戏,脚本有时会返回4000页评论(每个html页面包含20条评论)、6000页或500页,直到停止
我设想的解决方案可能是保存脚本测试的每个URL,并在每次endre=re.compile(r'({“success”:2})|(no_more_reviews)
为真时重复循环10次,并跳过已收集的URL,但我不确定它是否真的有效
我也会在GitHub页面上提出这个问题,但作者似乎并不经常回应,我真的很想知道为什么会发生这种情况,以及是否有可能解决这个问题。先谢谢你
编辑:所以我看了一下Steam API文档,似乎每一页都有一个光标,可以加载下一页。那么为什么它会随机变化呢?就像是web服务器的一种基本自卫形式。Steam使用它来帮助防止其服务器被滥用(太多)
import argparse
import csv
import os
import re
import socket
import string
import urllib
import urllib.request
import urllib.parse
import json
from contextlib import closing
from time import sleep
def download_page(url, maxretries, timeout, pause):
tries = 0
htmlpage = None
while tries < maxretries and htmlpage is None:
try:
with closing(urllib.request.urlopen(url, timeout=timeout)) as f:
htmlpage = f.read()
sleep(pause)
except (urllib.error.URLError, socket.timeout, socket.error):
tries += 1
return htmlpage
def getgameids(filename):
ids = set()
with open(filename, encoding='utf8') as f:
reader = csv.reader(f)
for row in reader:
dir = row[0] #à la base c'est 0
id_ = row[1]
name = row[2]
ids.add((dir, id_, name))
return ids
def getgamereviews(ids, timeout, maxretries, pause, out):
urltemplate = string.Template(
'https://store.steampowered.com//appreviews/$id?cursor=$cursor&filter=recent&language=english')
endre = re.compile(r'({"success":2})|(no_more_reviews)')
for (dir, id_, name) in ids:
if dir == 'sub':
print('skipping sub %s %s' % (id_, name))
continue
gamedir = os.path.join(out, 'pages', 'reviews', '-'.join((dir, id_)))
donefilename = os.path.join(gamedir, 'reviews-done.txt') #When all reviews of a given have been extracted
if not os.path.exists(gamedir): #Create a folder if not existing
os.makedirs(gamedir)
elif os.path.exists(donefilename): #if folder exists, skip game
print('skipping app %s %s' % (id_, name))
continue
print(dir, id_, name)
cursor = '*'
offset = 0
page = 1
maxError = 10
errorCount = 0
i = 0
while True:
url = urltemplate.substitute({'id': id_, 'cursor': cursor})
print(offset, url)
htmlpage = download_page(url, maxretries, timeout, pause)
if htmlpage is None:
print('Error downloading the URL: ' + url)
sleep(pause * 3)
errorCount += 1
if errorCount >= maxError:
print('Max error!')
break
else:
with open(os.path.join(gamedir, 'reviews-%s.html' % page), 'w', encoding='utf-8') as f:
htmlpage = htmlpage.decode()
if endre.search(htmlpage):
break
f.write(htmlpage)
page = page + 1
parsed_json = (json.loads(htmlpage))
cursor = urllib.parse.quote(parsed_json['cursor'])
with open(donefilename, 'w', encoding='utf-8') as f:
pass
def main():
parser = argparse.ArgumentParser(description='Crawler of Steam reviews')
parser.add_argument('-f', '--force', help='Force download even if already successfully downloaded', required=False,
action='store_true')
parser.add_argument(
'-t', '--timeout', help='Timeout in seconds for http connections. Default: 180',
required=False, type=int, default=180)
parser.add_argument(
'-r', '--maxretries', help='Max retries to download a file. Default: 5',
required=False, type=int, default=3)
parser.add_argument(
'-p', '--pause', help='Seconds to wait between http requests. Default: 0.5', required=False, default=0.01,
type=float)
parser.add_argument(
'-m', '--maxreviews', help='Maximum number of reviews per item to download. Default:unlimited', required=False,
type=int, default=5000000)
parser.add_argument(
'-o', '--out', help='Output base path', required=False, default='data')
parser.add_argument(
'-i', '--ids', help='File with game ids', required=False, default='./data/games.csv')
args = parser.parse_args()
if not os.path.exists(args.out):
os.makedirs(args.out)
ids = getgameids(args.ids)
print('%s games' % len(ids))
getgamereviews(ids, args.timeout, args.maxretries, args.pause, args.out)
if __name__ == '__main__':
main()