这一篇主要做数据准备工作。解决象棋机器人的训练数据问题。
用scrapy编写个简单爬虫,爬取网上的数据,开始设计时比较简单,单线程抓取,爬了几天才爬了6万盘棋的数据。
棋谱收集站(http://game.onegreen.net/chess/Index.html)数据分析得取如下抓取的数据格式:
{'desc': '1999年全国象棋个人赛',
'init': '',
'move_list': '774770627967807089796364797310222625121646452042736330416362003062637275192730366748362617181666635366654746754529477077476577276547271748671718394823245355454425242624093918160605838439334454333554525545161367555254352524254525545246262201254501224525220125450122452522012545525426460122452554524626220125450122452522012545012245252210556352562646131625451022638440304535304035454030483916365948565484725414464336384323141923292234725130405143342645152618153538354335182635162618',
'name_black': '河北 阎文清',
'name_red': '浙江 陈寒峰',
'result': '和棋',
'title': '浙江 陈寒峰 和 河北 阎文清',
'url': 'http://game.onegreen.net/chess/HTML/27883.html'}
对残局的数据例子如下:
{'desc': '象棋实用残局第二集',
'init': '9999299949999999249999869999999958999999519999999999999999997699',
'move_list': '8685766685846667847458552444676874645559494868584838592964542925383725155453151737381713534313183837184844485848',
'name_black': '',
'name_red': '',
'result': '和棋',
'title': '第177局 车兵相和车卒',
'url': 'http://game.onegreen.net/chess/HTML/21806.html'}
字段说明
name_black:拿黑色棋的人
name_red: 拿红色棋的人
result:对局结果
url:数据收集对应的网页
desc: 棋局说明
title: 棋局标题
init: 初始棋局状态,残局时列出条个子的状态。每2伴数字代表一个棋子,值代码棋子对应的位置。(后面后说明)
move_list:下棋顺序列表, 每步棋用4个数字来表示。每两位代码位置信息,表示从哪个位置移到了哪个位置。如例子的第一步:“7747” 表示右炮移中线(当头炮)
棋盘位置说明
以左上角为中心点:坐标如下图,列代表第一位数字,行用第二位数字来表示, 如右上角的黑车的坐标为“80”,右下角的红车的坐标为“89”
棋子位置说明: init对应的值,长度64,每两半代码一个棋子。一共32个棋子。位置“99” 表示无效位置。
以例子中的init("9999299949999999249999869999999958999999519999999999999999997699")为例:
位置说明:
1 99 红车
2 99 红马
3 29 红相
4 99 红仕
5 49 红帅
6 99 红仕
7 99 红相
8 99 红马
9 24 红车
10 99 红炮
11 99 红炮
12 86 红兵
13 99 红兵
14 99 红兵
15 99 红兵
16 99 红兵
17 58 黑车
18 99 黑马
19 99 黑相
20 99 黑士
21 51 黑将
22 99 黑士
23 99 黑相
24 99 黑马
25 99 黑车
26 99 黑炮
27 99 黑炮
28 99 黑卒
29 99 黑卒
30 99 黑卒
31 76 黑卒
32 99 黑卒
对应的图为:
移动步数说明(move_list)
以残局例子为例("
8685766685846667847458552444676874645559494868584838592964542925383725155453151737381713534313183837184844485848")
8685 兵一进一(兵开始位置为 "86", 改变到位置"85")
7666 卒8平7 (卒从位置“76” 移到位置“66”)
...
scrapy 开始操作
具体安装scrapy操作网上有好多说明,这里就不说了。
爬虫代码
建立爬虫工程chessspider
scrapy startproject chessspider
修改items.py
import scrapy
class ChessDataItem(scrapy.Item):
url = scrapy.Field()
init = scrapy.Field()
name_black = scrapy.Field()
name_red = scrapy.Field()
title = scrapy.Field()
time = scrapy.Field()
move_list = scrapy.Field()
desc = scrapy.Field()
result = scrapy.Field()
pass
添加spiders/chessdataspider.py 添加自己的爬虫。
代码如下
__author__ = 'jerome'
import scrapy
from scrapy.selector import Selector
from scrapy.crawler import CrawlerRunner
from twisted.internet import reactor
from scrapy.settings import Settings
from scrapy.http import Request
import sys
sys.path.append("../..")
from chessspider.items import ChessDataItem
class ChessDataSpider(scrapy.Spider):
name = "chessdataspider"
#设置下载延时
download_delay = 2
allowed_domains = ["game.onegreen.net"]
start_urls = [
"http://game.onegreen.net/chess/Index.html",
# "http://game.onegreen.net/chess/HTML/21806.html"
]
def parse(self, response):
hxs = Selector(response)
title = hxs.xpath("//title/text()")[0].extract()
iframes = hxs.xpath("//iframe/@name").extract()
is_item = False
for iframe in iframes:
item = self.parse_item(title, iframe)
if len(item["move_list"]) > 0:
is_item = True
item["url"] = response.url
yield item
if not is_item and response.url.find("/chess/HTML/") < 0:
for url in hxs.xpath("//a/@href").extract():
if url.find("chess") >= 0:
print("ok url:", url)
if url.find("http://game.onegreen.net") >= 0:
yield Request(url, callback=self.parse)
else:
yield Request("http://game.onegreen.net" + url, callback=self.parse)
else:
print("error url:", url)
def get_Data(self, data, pattern):
result = []
start_index = 0
for _ in range(10):
start = data.find("[{}]".format(pattern), start_index, len(data))
if start < 0:
break
start += len("[{}]".format(pattern))
end = data.find("[/{}]".format(pattern), start_index, len(data))
if start < end:
result.append(data[start:end])
start_index = end + len("[/{}]".format(pattern))
if len(result) == 0:
return ""
return max(result, key=lambda k: len(result))
def parse_item(self, title, data):
item = ChessDataItem()
item["move_list"] = self.get_Data(data, "DhtmlXQ_movelist")
item["title"] = self.get_Data(data, "DhtmlXQ_title")
item["name_black"] = self.get_Data(data, "DhtmlXQ_black")
item["name_red"] = self.get_Data(data, "DhtmlXQ_red")
item["desc"] = self.get_Data(data, "DhtmlXQ_event")
item["result"] = self.get_Data(data, "DhtmlXQ_result")
item["init"] = self.get_Data(data, "DhtmlXQ_binit")
if len(item["title"]) == 0:
item["title"] = title
return item
if __name__ == '__main__':
print("*************************** \n\nmain function")
settings = Settings({'USER_AGENT': 'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1)'})
runner = CrawlerRunner(settings)
d = runner.crawl(ChessDataSpider)
reactor.run() # the script will block here until the crawling is finished
其中 main函数是为了测试爬取分析数据结果。 可直接使用"python chessdataspider.py" 进行运行测试
为防止被ban, 使用了两秒钟运行一次请求
download_delay = 2
最后运行爬虫
把结果值输出到当前录下的"item_new.json"文件中
scrapy crawl chessdataspider -o item_new.json
注: scrapy命令要在目录chessspider下才能正常运行。
正常命令运行顺序为:
scrapy startproject chessspider
cd chessspider/
。。。
scrapy crawl chessdataspider -o item_new.json