(1-2-02)数据采集:网络爬虫技术(1)

1.2.4  爬取体育新闻信息并保存到XML文件

在本节的内容中,将通过一个具体实例的是过程,详细讲解使用Python爬取新闻信息并保存到XML文件中的方法,然后讲解了使用Stanford CoreNLP提取XML数据特征关系的过程。

1. 爬虫抓取数据

在本项目的“Scrap”目录中提供了多个爬虫文件,每一个文件都可以爬取指定网页的新闻信息,并且都可以将爬取的信息保存到XML文件中。例如通过文件scrap1.py可以抓取新浪体育某个页面中的新闻信息,并将抓取的信息保存到XML文件news1.xml中。文件scrap1.py的主要实现代码如下所示。

源码路径:codes\1\1-2\pythonCrawler\venv\Scrap\scrap1.py

doc=xml.dom.minidom.Document()
root=doc.createElement('AllNews')
doc.appendChild(root)
#用于爬取新浪体育的网页
urls2=['http://sports.example..com.cn/nba/25.shtml']

def scrap():
    for url in urls2:
        count = 0  # 用于统计总共爬取新闻数量
        html = urlopen(url).read().decode('utf-8')
        #print(html)
        res=re.findall(r'<a href="(.*?)" target="_blank">(.+?)</a><br><span>',html)#用于爬取超链接和新闻标题

        for i in res:
            try:
                urli=i[0]
                htmli=urlopen(urli).read().decode('utf-8')
                time=re.findall(r'<span class="date">(.*?)</span>',htmli)
                resp=re.findall(r'<p>(.*?)</p>',htmli)
                #subHtml=re.findall('',htmli)
                nodeNews=doc.createElement('News')
                nodeTopic=doc.createElement('Topic')
                nodeTopic.appendChild(doc.createTextNode('sports'))
                nodeLink=doc.createElement('Link')
                nodeLink.appendChild(doc.createTextNode(str(i[0])))
                nodeTitle=doc.createElement('Title')
                nodeTitle.appendChild(doc.createTextNode(str(i[1])))
                nodeTime=doc.createElement('Time')
                nodeTime.appendChild(doc.createTextNode(str(time)))
                nodeText=doc.createElement('Text')
                nodeText.appendChild(doc.createTextNode(str(resp)))
                nodeNews.appendChild(nodeTopic)
                nodeNews.appendChild(nodeLink)
                nodeNews.appendChild(nodeTitle)
                nodeNews.appendChild(nodeTime)
                nodeNews.appendChild(nodeText)
                root.appendChild(nodeNews)
                print(i)
                print(time)
                print(resp)
                count+=1
            except:
                print(count)
                break
scrap()
fp=open('news1.xml','w', encoding="utf-8")
doc.writexml(fp, indent='', addindent='\t', newl='\n', encoding="utf-8")

执行后会将抓取的新浪体育的新闻信息保存到XML文件news1.xml中,如图1-6所示。

图1-6  文件news1.xml

2. 使用Stanford CoreNLP提取XML数据的特征关系

Stanford CoreNLP是由斯坦福大学开源的一套Java NLP工具,提供了词性标注(part-of-speech (POS) tagger)、命名实体识别(named entity recognizer (NER))、情感分析(sentiment analysis)等功能。Stanford CoreNLP为Python提供了对应的模块,可以通过如下命令进行安装:

pip install stanfordcorenlp

因为本项目抓取到是中文信息,所以我们还需要在Stanford CoreNLP官网下载专门处理中文的软件包,例如stanford-chinese-corenlp-2018-10-05-models.jar。

编写文件nlpTest.py,功能是调用Stanford CoreNLP分析处理上面抓取到的数据文件news1.xml,提取出数据中的人名、城市和组织等信息,主要实现代码如下所示。

源码路径:codes\1\1-2\pythonCrawler\venv\nlpTest.py

import os
from stanfordcorenlp import StanfordCoreNLP

nlp = StanfordCoreNLP(r'H:\stanford-corenlp-full-2018-10-05', lang='zh')

for line in open(r'H:\pythonshuju\2\1-5\pythonCrawler-master\venv\Scrap\news1.xml','r',encoding='utf-8'):
    res=nlp.ner(line)
    person = ["PERSON:"]
    location=['LOCATION:']
    organization=['ORGNIZATION']
    gpe=['GRE']
    for i in range(len(res)):
        if res[i][1]=='PERSON':
            person.append(res[i][0])
    for i in range(len(res)):
        if res[i][1]=='LOCATION':
            location.append(res[i][0])
    for i in range(len(res)):
        if res[i][1]=='ORGANIZATION':
            organization.append(res[i][0])
    for i in range(len(res)):
        if res[i][1]=='GPE':
            gpe.append(res[i][0])
    print(person)
    print(location)
    print(organization)
    print(gpe)


nlp.close()

执行后会输出提取分析后的数据:

['PERSON:', '凯文', '杜兰特', '杜兰特', '金州', '杜兰特', '曼尼-迪亚兹', 'Manny', 'Diaz', '迪亚兹', '杜兰特', '杜兰特', '威廉姆森', '巴雷特和卡', '雷蒂什']

['LOCATION:']

['ORGNIZATION', 'NCAA', '球队', '迈阿密', '大学', '橄榄', '球队', '迈阿密', '大学', '新任', '橄榄球队', '迈阿密', '先驱者', '报', '杜克大学']

#######后面省略好多信息

1.2.5  抓取XX百科

本实例文件baike.py能够抓取XX百科网站中的热门信息,具体功能如下所示。

  1. 抓取XX百科热门段子。
  2. 过滤带有图片的段子。
  3. 每当按一次回车键,显示一个段子的发布时间、发布人、段子内容和点赞数。

源码路径:daima\1\1-2\baike.py

(1)确定URL并抓取页面代码。

首先我们确定好页面的URL是 http://www.域名主页.com/hot/page/1,其中最后一个数字1代表当前的页数,可以传入不同的值来获得某一页的段子内容。首先编写代码设置要抓取的目标首页和user_agent值,具体实现代码如下所示。

import urllib
import urllib.request
page = 1
url = 'http://www.域名主页.com/hot/page/' + str(page)
user_agent = 'Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)'
headers = { 'User-Agent' : user_agent }
try:
    request = urllib.request.Request(url,headers = headers)
    response = urllib.request.urlopen(request)
    print (response.read())
except (urllib.request.URLError, e):
    if hasattr(e,"code"):
        print (e.code)
    if hasattr(e,"reason"):
        print e.reason

执行后会打印输出第一页的HTML代码。

(2)提取某一页的所有段子。

在获取了HTML代码后,接下来开始获取某一页的所有段子。首先我们审查元素看一下,按浏览器的F12,截图如图1-7所示。

图1-7  http://www.域名主页.com/hot/的源码

由此可见,每一个段子都是被<div class="articleGender manIcon">…</div>包含的内容。如果想获取页面中的发布人、发布日期、段子内容以及点赞的个数,需要注意有些段子是带有图片的。因为不能在控制台中显示图片信息,所以需要删除带有图片的段子,确保只保存只含文本的段子。为了实现这一功能,使用正则表达式方法re.findall()寻找所有匹配的内容。编写的正则表达式匹配语句如下所示。

        pattern = re.compile(

            '<div.*?author clearfix">.*?<h2>(.*?)</h2>.*?<div.*?content".*?<span>(.*?)</span>.*?</a>(.*?)<div class= "stats".*?number">(.*?)</i>', re.S)

上述正则表达式是整个程序的核心,本实例的实现文件是baike.py,具体实现代码如下所示。

import urllib.request
import re
class Qiubai:

    # 初始化,定义一些变量
    def __init__(self):
        # 初始页面为1
        self.pageIndex = 1
        # 定义UA
        self.user_agent = 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.75 Safari/537.36'
        # 定义headers
        self.headers = {'User-Agent': self.user_agent}
        # 存放段子的变量,每一个元素是每一页的段子
        self.stories = []
        # 程序是否继续运行
        self.enable = False
    def getPage(self, pageIndex):
        """
        传入某一页面的索引后的页面代码
        """
        try:
            url = 'http://www.域名主页.com/hot/page/' + str(pageIndex)
            # 构建request
            request = urllib.request.Request(url, headers=self.headers)
            # 利用urlopen获取页面代码
            response = urllib.request.urlopen(request)
            # 页面转为utf-8编码
            pageCode = response.read().decode("utf8")
            return pageCode
        # 捕获错误原因
        except (urllib.request.URLError, e):
            if hasattr(e, "reason"):
                print (u"连接糗事百科失败,错误原因", e.reason)
                return None
    def getPageItems(self, pageIndex):
        """
        传入某一页代码,返回本页不带图的段子列表
        """
        # 获取页面代码
        pageCode = self.getPage(pageIndex)
        # 如果获取失败,返回None
        if not pageCode:
            print ("页面加载失败...")
            return None
        # 匹配模式
        pattern = re.compile(
            '<div.*?author clearfix">.*?<h2>(.*?)</h2>.*?<div.*?content".*?<span>(.*?)</span>.*?</a>(.*?)<div class= "stats".*?number">(.*?)</i>', re.S)
        # findall匹配整个页面内容,items匹配结果
        items = re.findall(pattern, pageCode)
        # 存储整页的段子
        pageStories = []
        # 遍历正则表达式匹配的结果, 0 name, 1 content, 2 img, 3 votes
        for item in items:
            # 是否含有图片
            haveImg = re.search("img", item[2])
            # 不含,加入pageStories
            if not haveImg:
                # 替换content中的<br/>标签为\n
                replaceBR = re.compile('<br/>')
                text = re.sub(replaceBR, "\n", item[1])
                # 在pageStories中存储:名字、内容、赞数
                pageStories.append(
                    [item[0].strip(), text.strip(), item[3].strip()])
        return pageStories
    def loadPage(self):
        """
        加载并提取页面的内容,加入到列表中
        """
        # 如未看页数少于2,则加载并抓取新一页补充
        if self.enable is True:
            if len(self.stories) < 2:
                pageStories = self.getPageItems(self.pageIndex)
                if pageStories:
                    # 添加到self.stories列表中
                    self.stories.append(pageStories)
                    # 实际访问的页码+1
                    self.pageIndex += 1
    def getOneStory(self, pageStories, page):
        """
        调用该方法,回车输出段子,按下q结束程序的运行
        """
        # 循环访问一页的段子
        for story in pageStories:
            # 等待用户输入,回车输出段子,q退出
            shuru = input()
            self.loadPage()
            # 如果用户输入q退出
            if shuru == "q":
                # 停止程序运行,start()中while判定
                self.enable = False
                return
            # 打印story:0 name, 1 content, 2 votes
            print (u"第%d页\t发布人:%s\t\3:%s\n%s" % (page, story[0], story[2], story[1]))
    def start(self):
        """
        开始方法
        """
        print (u"正在读取糗事百科,回车查看新段子,q退出")
        # 程序运行变量True
        self.enable = True
        # 加载一页内容
        self.loadPage()
        # 局部变量,控制当前读到了第几页
        nowPage = 0
        # 直到用户输入q,self.enable为False
        while self.enable:
            if len(self.stories) > 0:
                # 吐出一页段子
                pageStories = self.stories.pop(0)
                # 用于打印当前页面,当前页数+1
                nowPage += 1
                # 输出这一页段子
                self.getOneStory(pageStories, nowPage)
if __name__ == '__main__':
    qiubaiSpider = Qiubai()
    qiubaiSpider.start()

执行效果如图1-8所示,每次按下回车键就会显示下一条热门糗事信息。

图1-8  执行效果

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

码农三叔

感谢鼓励

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值