Python 3网络爬虫学习笔记(4)——开始采集

一.遍历单个域名

像之前一样,我们还是利用维基百科来进行数据采集的学习
通过对维基百科上python词条的HTML源代码的观察,可以发现页面有关的链接都在标签名为a的href属性之中:
在这里插入图片描述
如图,就是指向消歧义的链接。而这样的链接又分为内链和外链:

内链:同一网站不同内容页面之间的相互链接。内链就是网站域名下内容页面之间的链接,内链优化的好,网站的结构就会好,也就会有利于网站的优化。
外链:从别的网站导入到自己网站的链接,就是外部网站有内容链接指向到你的网站。外链意味着别的网站对自己网站的一种投票,这种投票就是网站的权重,所以多样化的外链可以增加网站的权重。带超链接的URL跟纯文本链接最大的区别就是这些链接可以有效的吸引蜘蛛爬行你的网站。

通过之前的学习,我们已经学会了如何去获取维基百科的一个页面并且提取页面链接:

from urllib.request import urlopen
from bs4 import BeautifulSoup

html = urlopen("http://en.wikipedia.org/wiki/Kevin_Bacon")
bsObj = BeautifulSoup(html)
for link in bsObj.findAll("a"):
	if 'href' in link.attrs:
		print(link.attr['href'])

如果我们观察生成的一串链接,会发现除了一些指向词条的链接,还会有一些我们不需要的链接;维基百科的每个页面都充满了侧边栏,页眉,页脚链接,以及连接到分类页面、对话页面和其他不包含词条的页面的链接。但是如果能够仔细观察那些指向词条页面的链接,会发现它们有几个共同点:

  • 他们都在id是bodyContent的div标签中
  • URL链接没有冒号
  • URL链接以/wiki/开头

利用这些规则,我们可以调整一下代码,让它去寻找能够从Kevin_Bacon词条页面中到达的所有其他词条的链接:

  • 首先,导入需要的Python库
  • 其次,用系统的当前时间生成一个随机数生成器,这样可以保证每次程序运行的时候,维基百科词条的选择都是一个全新的随机路径。
  • 然后,定义一个getLinks函数,其参数是词条的/wiki/<词条名称>形式的URL链接,再加上维基百科的域名,再用域名中的网页获取一个BeautifulSoup对象。
  • 写一个主函数,把起始页面的词条链接设置成列表,然后用一个循环,从页面中随机找一个词条链接并获取href属性,打印这个链接,再把这个链接传入getLinks函数,重新获取新的链接列表。
    代码如下:
from urllib.request import urlopen
from bs4 import BeautifulSoup
import datetime
import random
import re

random.seed(datetime.datetime.now())
def getLinks(articleUrl):
    html = urlopen("http://en.wikipedia.org"+articleUrl)
    bsObj = BeautifulSoup(html,"html.parser")
    return bsObj.find("div", {"id":"bodyContent"}).findAll("a",href=re.compile("^(/wiki/)((?!:).)*$"))

links = getLinks("/wiki/Kevin_Bacon")
while len(links)>0:
    newArticle = links[random.randint(0,len(links)-1)].attrs["href"]
    print(newArticle)
    links = getLinks(newArticle)

其中,random.randint(a, b),用于生成一个指定范围内的整数。其中参数a是下限,参数b是上限。
运行一下这段代码,结果如下:
在这里插入图片描述

二.采集整个网站

通过上面的学习,我们已经可以在单个网站上从一个链接跳到另一个链接。但是如果想要系统的把整个网站按目录分类,或者要搜索网站上的每一个页面,那就需要采集整个网站,这是一个十分耗费内存资源的过程,最适合的工具就是用一个数据库来存储采集的资源,现在先不谈这些。
下面我们来创建一个爬虫,采集维基百科网站上所有页面标题,正文的第一个段落。

为了避免一个页面被采集两次,链接去重是很有必要的,这里可以用python中的set数据结构,这个结构在c++乙级JAVA中都存在,代表一个没有重复元素的集合。

像之前一样,通过对一些页面的观察,我们可以总结出一些规则,拟定一个采集模式:

  • 所有的标题都在h1->span标签之中
  • 想要获取第一段文字,可以用div#mw-content-text——>p。
  • 编辑链接只会出现在词条页面上。如果有编辑链接,都位于li#ca-edit——>span——>a里面。

如此一来,我们就可以写出一个收集数据的爬虫:

from urllib.request import urlopen
from bs4 import BeautifulSoup
import re

pages = set()
def getLinks(pageUrl):
    global pages
    html = urlopen("http://en.wikipedia.org"+pageUrl)
    bsObj = BeautifulSoup(html)
    try:
        print(bsObj.h1.get_text())
        print(bsObj.find(id="mw-content-text").findAll("p")[0])
        print(bsObj.find(id="ca-edit").find("span").find("a").attrs['href'])
    except AttributeError:
        print("页面缺少一些属性!不过不用担心!")

    for link in bsObj.findAll("a", href=re.compile("^(/wiki/)")):
        if 'href' in link.attrs:
            if link.attrs['href'] not in pages:
                #我们遇到了新页面
                newPage = link.attrs['href']
                print("-----------------------------\n"+newPage)
                pages.add(newPage)
                getLinks(newPage)

getLinks("")

这段代码中从getLinks("")开始,也就是维基百科的主页面开始,递归调用getLinks()函数,对维基百科的数据进行采集。运行一下,结果如下:
在这里插入图片描述

三.通过互联网采集

在之前的爬虫示例中,我们忽略掉了外链,但是如果我们再忽略外链,而是跟着他跳转,这会是一个全新的挑战,因为许多网站的布局是十分不同的,这就意味着我们的查找方式要十分灵活。
用几个灵活的Python函数就可以实现不同类型的网络爬虫,从书中的示例网站https://oreilly.com开始访问:

from urllib.request import urlopen
from urllib.parse import urlparse
from bs4 import BeautifulSoup
import re
import datetime
import random

pages = set()
random.seed(datetime.datetime.now())

#获取页面所有内链的列表
def getInternalLinks(bsObj, includeUrl):
    includeUrl = urlparse(includeUrl).scheme+"://"+urlparse(includeUrl).netloc
    internalLinks = []
    #找出所有以"/"开头的链接
    for link in bsObj.findAll("a",href=re.compile("^(/|.*"+includeUrl+")")):
        if link.attrs['href'] is not None:
            if link.attrs['href'] not in internalLinks:
                if(link.attrs['href'].startswitch("/")):
                    internalLinks.append(includeUrl+link.attrs['href'])
                else:
                    internalLinks.append(link.attrs['href'])
    return internalLinks

#获取页面所有外链列表
def getExternalLinks(bsObj, excludeUrl):
    externalLinks = []
    #找出所偶遇以www或者http开头且不包含当前URL的连接
    for link in bsObj.findAll("a",href=re.compile("^(http|www)((?!"+excludeUrl+").)*$")): 
        if link.attrs['href'] is not None:
            if link.attrs['href'] not in externalLinks:
                externalLinks.append(link.attrs['href'])
    return externalLinks

def splitAddress(address):
    addressParts = address.replace("http://","").split("/")
    return addressParts

#返回一个随机的外链
def getRandomExternalLink(startingPage):
    html = urlopen(startingPage)
    bsObj = BeautifulSoup(html)
    externalLinks = getExternalLinks(bsObj, urlparse(startingPage).netloc)
    if len(externalLinks) == 0:
        print("No external links")
        domain = urlparse(startingPage).scheme+"://"+urlparse(startingPage).netloc
        internalLinks = getInternalLinks(bsObj, domain)
        return getRandomExternalLink(internalLinks[random.randint(0,len(internalLinks)-1)])
    else:
        return externalLinks[random.randint(0,len(externalLinks)-1)]

def followExternalOnly(startingSite):
    externalLink = getRandomExternalLink(startingSite)
    print("Random external link is: "+externalLink)
    followExternalOnly(externalLink)

followExternalOnly("https://oreilly.com")

运行一下,部分结果如下:
在这里插入图片描述
不过,由于国家的相关政策,可能会出现这样的情况:
在这里插入图片描述
不过没有关系,我们已经学会了如何在互联网上去采集数据。

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值