关于scrapy:scrapy三-爬取ConceptNet

4次阅读

共计 4946 个字符,预计需要花费 13 分钟才能阅读完成。

一、工作背景

放弃之前的打算
通过开会讨论,仅爬取心理学畛域的常识来构建心理沙盘的常识图谱是不可取的(事实上,我的项目的指标是依据用户设计的沙盘场景 推理出 用户的心理状态,而不是做心理学百科知识的科普)。
这一常识图谱构建方向上的扭转归功于咱们小组的探讨和 《常识图谱 - 概念与技术》 这本书对我的启发,一些常识要点如下(能够跳过间接查看我的项目细节):

1、常识图谱狭义概念

为了讲明确我对我的项目的了解,还要从人工智能说起(想在这里一次性的的捋分明,兴许未来会退出一个链接):

图:常识图谱的学科位置

人工智能有 3 个学派:符号主义 连贯主义 行为主义
常识工程 源于 符号主义 ,为了无效的利用常识,首先要在计算机系统中正当的示意常识;
常识示意 的一个重要形式就是 常识图谱
留神:常识图谱只是常识示意中的一种,除了语义网络以外,谓词逻辑、产生式规定、本体、框架、决策树、贝叶斯网络、马尔可夫逻辑网都是常识示意的模式;

2、KG 钻研意义

KG 是认知智能的基石

  • 机器了解数据的实质是从 数据 到常识图谱中的 常识因素 (实体概念、关系)的 映射
  • 了解过程 能够视作建设从 数据 (文本、图片、语音、视频)到 KG(脑海)中 实体、概念、属性 之间的 映射 过程;

几点 KG 对于认知智能的重要性:
①机器语言认知
人类对语言的了解建设在认知能力根底之上,所以解释了咱们听不懂东方的笑话故事。
语言了解须要背景常识,机器了解 自然语言当然也须要 背景常识 了。
②赋能可解释人工智能
“解释”与符号化常识图谱密切相关,人只能了解符号而无奈了解数值,书中有三个很好的例子:

问鲨鱼为什么可怕?你可能解释因为鲨鱼是肉食动物——用概念在解释;问鸟为什么能翱翔?你可能会解释因为它有翅膀——用属性解释;问前段时间鹿晗和关晓彤为什么会刷屏?你可能会解释因为她是他女朋友——用关系在解释;

③有助于加强机器学习的能力
人类的学习高效、强壮,并不需要机器学习那样宏大的样本量,根本原因在于 人类很少从零开始学习,人类善于联合丰盛的先验常识。

3、常识常识图谱

常识图谱能够依据其所涵盖的常识分为四类:事实常识、概念常识、词汇常识和常识常识
有了 1 和 2 的铺垫,咱们抉择为心理沙盘我的项目建设一个 常识常识图谱 的理由就明确了,每一个沙盘实体都来自事实世界,咱们要做的是 高度还原人脑 中实体与实体、实体与概念、概念与概念这些映射关系,从而可能让 心理测试后果更健壮和有说服力
以沙盘形容中的虾为例:

①虾看似慢吞吞的游,遇到对手时它骁勇奔下来舒展双臂与它博斗,打断双臂在所不惜,象征充斥阳刚之气。②虾须晦涩,飘逸,虾尾随移其形而动,象征共性和指标。

咱们能够构建这样一种分割:

二、工作思路

沙盘心理分析师提供了一个 entity 文件,里边有大概 600 个实体,长这样:

1、以这 600 个实体为根节点,从 ConceptNet 上爬取实体的所有关系,再递归关系所牵的另一边实体,提取的内容有:
entity1、relation、entity2、att1、att2、weight、id
此七项内容
2、依据这些内容先构建一个常识图谱

三、scrapy 实操

1、在之前创立的爬虫工程中,新建一个 spider 文件,还是通过命令来实现(工程项目创立的具体操作步骤参考 scrapy(一) 爬取心理学畛域词汇)

scrapy genspider -t basic conceptSpider api.conceptnet.io

2、剖析页面,试探爬取——周而复始直到成型

①方法论:
当有一个新的爬虫工作时,我 必然不能也绝不可能 做到间接写成 spider 文件,解正文 settings.py 中的 pipline,写 items.py 文件,写 pipline.py 文件。因为网页是否容许爬取、网页的格局、咱们想要的内容在哪、是否须要递归等,都须要去 debug,缓缓的磨进去。也就是 先别解正文 settings.py 中的 pipline,只消应用命令
$ scrapy crawl conceptSpider --nolog
一步步的迭代测验 咱们的规定是否写对了。
②页面剖析:
查看 ConceptNet 官网文档,得悉这种 api 类型的网页中,每一个 key 代表什么。
先给 start_urls 中只增加了一个‘http://api.conceptnet.io/c/en/apple’
来专一于剖析这个页面,显然每一页都有共性,页面局部截图如下:

其中’edges‘是一个 list 模式,每一项是一个关系,连贯着与 apple 相干的实体;
在网页底部,有一个键值是‘view’,是管前后页的,也就是说里边有下一页的 url;

网页解析:

# 将 json 格局的 api 页面解析为用字符串示意的字典
js_str=response.xpath("//pre").xpath("string(.)").extract()[0].strip()
# 将字符串转化为字典
js=json.loads(js_str)

规则化提取:

for edge in js['edges']:
    str=edge['@id'].replace('[','').replace(']','').replace('/a/','').split(',')
    # 只取英文关系
    if str[1][3:6]!='en/' or str[2][3:6]!='en/':
        continue
    # 取两个实体
    e1=str[1].replace('/c/en/','').replace('/n','').replace('/wn','')[:-1].split('/',1)
    e2=str[2].replace('/c/en/','').replace('/n','').replace('/wn','')[:-1].split('/',1)
    # 取关系
    r=str[0].replace('/r/','')[:-1]
    # 摘除后边的标识,放到属性里
    e1_att=''e2_att=''
    if len(e1)==2:
        e1_att=e1[1]
    if len(e2)==2:
        e2_att=e2[1]

注:测试规则化提取失常后,就能够在上边的 for 循环里边用 item 传给 pipline 去输入了。
③页面跳转

if 'view' in js and 'nextPage' in js['view']:
    relPos = js['view']['nextPage']
    print("进入衍生页面执行 parse:")
    nexthref = self.url_std+relPos
    print(nexthref)
    yield scrapy.Request(nexthref, callback=self.parse)

留神:这里有坑,如果只判断 'nextPage' in js['view'],你就会发现 有的实体页面中没有 view 这个键值,会产生谬误,但不影响 pipline 输入的后果。

*3、解决网页拜访频率限度问题

发现问题:
在只有两个 url 的 start_urls 列表上测试没有问题,然而将 400 多个 url 放入 start_urls 中后,失去的后果每次都只有几百,几千条,最多 1 万条。
细节: 察看输入文件 triplets.txt 和终端显示,发现每次在第 602 个页面,第 1206 个页面呈现卡壳,最终可能进行输入。
波折的解决过程:
认为是输入的时候内存爆了导致 write 文件终止或者是 txt 文件大小有限度导致提前敞开文件。后果换成 csv 格局也不好使,都要思考应用数据库了;

去掉 --nolog 后执行 $ scrapy crawl conceptSpider,发现问题在于 爬取阶段 而不是 pipline 的数据写出阶段。日志中有一些 url 反馈后果是 [429],也就是页面被拜访频繁导致的回绝拜访。

解决办法的帖子 具体如下:
可应用 429 状态码,同时蕴含一个 Retry-After 响应头用于通知客户端多长时间后能够再次申请服务。

middlewares.py: # 当状态码是 429 的时候 爬虫暂停 60 秒再爬取

import time
from scrapy.downloadermiddlewares.retry import RetryMiddleware
from scrapy.utils.response import response_status_message

class TooManyRequestsRetryMiddleware(RetryMiddleware):
    def __init__(self, crawler):
        super(TooManyRequestsRetryMiddleware, self).__init__(crawler.settings)
        self.crawler = crawler
    @classmethod
    def from_crawler(cls, crawler):
        return cls(crawler)
    def process_response(self, request, response, spider):
        if request.meta.get('dont_retry', False):
            return response
        elif response.status == 429:
            self.crawler.engine.pause()
            print("速度太快  暂停 60 秒")
            time.sleep(60)  # If the rate limit is renewed in a minute, put 60 seconds, and so on.
            self.crawler.engine.unpause()
            reason = response_status_message(response.status)
            return self._retry(request, reason, spider) or response
        elif response.status in self.retry_http_codes:
            reason = response_status_message(response.status)
            return self._retry(request, reason, spider) or response
        return response

settings.py:

DOWNLOADER_MIDDLEWARES = {  # 开启暂停中间件
   'psySpider.middlewares.PsyspiderDownloaderMiddleware': 543,
}

RETRY_HTTP_CODES = [429, 500, 403]  # 这个状态重试

DOWNLOAD_DELAY = 0.5
RANDOMIZE_DOWNLOAD_DELAY = True # 发完一个申请 随机暂停
4、piplines.py 写法
# 间接照着爬虫名字补上就能够
class ConceptspiderPipline(object):
    def __init__(self):
        self.file = open("./triplets1.csv","w",encoding='utf-8')
        self.writer = csv.writer(self.file, dialect="excel")

    def process_item(self, item, spider):
        theme=item['theme']
        entity_1 = item['entity_1']
        entity_2 = item['entity_2']
        relation = item['relation']
        att1=item['e1_att']
        att2=item['e2_att']
        weight=item['weight']
        id=item['id']
        self.writer.writerow([entity_1,entity_2,relation,att1,att2,weight,id])
        return item
5、items.py
# 间接照着爬虫名字补上就能够
class ConceptspiderItem(scrapy.Item):
    theme=scrapy.Field()
    entity_1=scrapy.Field()
    entity_2=scrapy.Field()
    relation=scrapy.Field()
    e1_att=scrapy.Field()
    e2_att=scrapy.Field()
    weight=scrapy.Field()
    id=scrapy.Field()
正文完
 0