这一篇主要做数据准备工作。解决象棋机器人的训练数据问题。
用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