利用scrapy框架爬取互动百科的词条--存成json

版权声明:欢迎评论和转载,转载请注明来源 https://blog.csdn.net/headwind_/article/details/70148539

最近课程作业需要爬取互动百科的词条,花了一周时间先学习了一点scrapy,利用该框架写了一个简单的爬虫
scrapy框架学习:Scrapy入门教程
首先看一下题目的要求:
1.首先实现了一个自动爬取互动百科词条页面的爬虫程序获取百科页面数据。根据互动百科的分类页面来获取对应分类下的所有词条页面。
2.为了构建知识,可以利用百科词条页面结构抽取出了互动百科的中的结构化知识。此外,参考给出的文献中的一种面向中文的开放域实体关系抽取方法,充分利用页面非结构化部分内容包含的知识。
3.为了管理从互动百科页面中抽取出来的知识,要实现一个知识存储系统,该系统接受实体关系三元组作为输入,将之存入以RDF文件格式存储的知识库中。
参考论文:面向互动百科的知识抽取和知识库构建方法研究

这里先实现爬取部分,互动百科的分类页面如下
http://fenlei.baike.com/
这里写图片描述
那么接下来就是以这些分类下的所有词条作为爬取的对象,其中所有词条对应的URL为:
http://fenlei.baike.com/分类名/list/
例如:
http://fenlei.baike.com/科学家/list/
接下来对应的词条页面里面我们需要爬取的是
1、词条名
2、开放分类
3、简介
4、基本信息
这里写图片描述

这里先按论文所说存成json格式的数据,比如当前词条为:

{
        "infoBox": {
                "籍贯:": "台湾", 
                "性别:": "男", 
                "经纪公司:": "杰威尔音乐有限公司", 
                "出生年月:": "1979年1月18日", 
                "主要成就:": "两届台湾金曲奖最佳国语男歌手获得十五座金曲奖(获奖最多)连续7年获得IFPI香港唱片销量大奖十大销量国语唱片", 
                "出生地:": "台湾新北", 
                "毕业院校:": "淡江中学", 
                "英文名:": "Jay Chou", 
                "民族:": "汉族",
                 "血型:": "O", 
                "职业:": "歌手、音乐人、制作人、导演、商人", 
                "星座:": "魔羯座", 
                "国籍:": "中国", 
                "代表作品:": "龙卷风、简单爱、七里香、夜曲、青花瓷、稻香、头文字D、不能说的秘密、逆战、青蜂侠、天台",
                 "中文名:": "周杰伦", 
                 "别名:": "周董"
         },
          "openType:": ["艺人"], 
          "infoName:": "周杰伦"
  }

词条页面的分析:首先词条名我是根据url来获得,因为互动百科词条对应的url为:
http://www.baike.com/wiki/词条名
因此只需要截取到url后面的词条名即可

sel = Selector(response)
#26就是 http://www.baike.com/wiki/ 的长度
pagename = response.url[26:]
'''
去掉不需要的信息,例如:http://www.baike.com/wiki/%E5%91%A8%E6%9D%B0%E4%BC%A6&prd=button_doc_entry
'''
pgnametxt = re.findall(r'.*[&|?]', pagename)
if len(pgnametxt) != 0:
    pagename = pgnametxt[0][:-1]
'''
url解码,主要是因为中文的原因,不解码的话就是这样子的%E5%91%A8%E6%9D%B0%E4%BC%
'''
pagename = urllib.unquote(pagename)
pgnameList = []
pgnameList.append(pagename)

接着开放分类以及基本信息框
这里写图片描述

cla = sel.xpath('//div[@class="place"]/p/a/@title').extract()

这里写图片描述

sites = sel.xpath('//div[@class="module zoom"]/table/tr/td').extract()

爬虫程序:

# -*- coding: utf-8 -*-
import scrapy
from scrapy.selector import Selector
import re
from bs4 import BeautifulSoup
import urllib

class W1Spider(scrapy.Spider):
    name = "w1"  # 爬虫的名字
    allowed_domains = ["www.baike.com", "fenlei.baike.com"]  # 允许爬的网站
    start_urls = [
        "http://fenlei.baike.com"  # 起始url
    ]

    def parse(self, response):
        namelist = []
        sel = Selector(response)
        classpage = sel.xpath(
            '//div[@class="td w-578"]/dl').extract()
        for i in range(len(classpage)):
            classname = re.findall(r'>.*?<', classpage[i])
            mess = {}
            for num in range(len(classname)):
                st = classname[num][1:-1]
                print (st)
                if st == "" or st == " | ":
                    pass
                else:
                    namelist.append(classname[num][1:-1])
        for count in range(len(namelist)):
            url = "http://fenlei.baike.com/"+ namelist[count]+"/list"
            print (url)
            yield scrapy.Request(url, callback=self.parse_onepage)


    def parse_onepage(self, response):
        soup = BeautifulSoup(response.body, "html.parser")
        total = soup.find_all('a', href=re.compile(
            r'http://www.baike.com/wiki/.+'))
        for href in total:
            try:
                # print href.attrs['href'][26:]
                # 递归爬取,不加callback试过也可以
                yield scrapy.Request(href.attrs['href'], callback=self.parse_w1)
            except:
                continue

    def parse_w1(self, response):
        soup = BeautifulSoup(response.body, "html.parser")
        # 如果不是http://www.baike.com/wiki/说明不是词条,则不作处理不保存信息
        total = soup.find_all('a', href=re.compile(
            r'http://www.baike.com/wiki/.+'))
        if len(total) != 0:
            name = {}
            infoDict = {}
            childinfoDict = ""

            sel = Selector(response)
            pagename = response.url[26:]
            pgnametxt = re.findall(r'.*[&|?]', pagename)
            if len(pgnametxt) != 0:
                pagename = pgnametxt[0][:-1]
            pagename = urllib.unquote(pagename)
            pgnameList = []
            pgnameList.append(pagename)

            sites = sel.xpath(
                '//div[@class="module zoom"]/table/tr/td').extract()
            cla = sel.xpath('//div[@class="place"]/p/a/@title').extract()

            infoDict.setdefault("infoName", ''.join(pgnameList))  # 词条名
            infoDict.setdefault("openType", ','.join(cla))  # 开放分类
            for site in range(len(sites)):
                #link = site.xpath('p/a/@title').extract()
                Key = re.findall(r'>.*</strong>', sites[site])
                Text = re.findall(r'>.*</span>', sites[site])
                # print Key
                lastTest = ""
                lastKey = ""
                test1 = ""
                test2 = ""
                if len(Key) != 0:
                    # for num in range(len(Key)):
                    keytxt = re.findall(r'>.*</a>', Key[0][1:-9])
                    # print keytxt
                    if len(keytxt) != 0:
                        lastKey = keytxt[0][1:-5]
                    else:
                        lastKey = Key[0][1:-10]
                    # print lastKey

                if len(Text) != 0:
                    for num in range(len(Text)):
                        t1 = re.findall(r'>.*?<', Text[num][:-6])
                        for i in range(len(t1)):
                            lastTest = lastTest + t1[i][1:-1]
                    rtxt = re.compile(r'"')
                    lastTest = rtxt.sub('', lastTest)
                    # print lastTest
                else:  # span 中没有内容
                    Text = re.findall(r'>.*</a>', sites[site])
                    # print Text
                    if len(Text) != 0:
                        lastTest = Text[0][1:-4]
                        # print lastTest
                        t1 = re.findall(r'">.*', lastTest)
                        # print t1
                        lastTest = t1[0][2:]
                    rtxt = re.compile(r'"')
                    lastTest = rtxt.sub('', lastTest)
                    # print lastTest
                # 先把它存成字典再作为值给键infobox
                #childinfoDict.setdefault(lastKey, lastTest)
                print (lastKey)
                print (lastTest)
                if lastKey != "" and lastKey != "":
                    if len(sites) != 1:
                        childinfoDict = childinfoDict + lastKey + ":" + lastTest + "@#$#"
                    else:
                        childinfoDict = childinfoDict + lastKey + ":" + lastTest
            #print (childinfoDict)
            infoDict.setdefault("infoBox", childinfoDict)  # 信息框的内容
            yield infoDict

pipelines.py

import json
import codecs

class MyspiderPipeline(object):
    def __init__(self):
        self.file = codecs.open('data.json', 'wb', encoding='utf-8')

    def process_item(self, item, spider):
        line = json.dumps(dict(item)) + ',' + '\n'
        self.file.write(line.decode("unicode_escape"))
        return item

到此简单的爬虫完成了,第一次写有很多不清楚的但先实现了功能,Python也不是很熟悉,期间遇到很多问题,最烦人的就是互动百科的每个词条里面的格式没有一个统一,比如这个就很坑人了本来以为只有一个空格,后来发现他和自己打的空格不一样彻底醉了。。。
这里写图片描述

阅读更多
换一批

没有更多推荐了,返回首页