VB.net 2010 视频教程 VB.net 2010 视频教程 python基础视频教程
SQL Server 2008 视频教程 c#入门经典教程 Visual Basic从门到精通视频教程
当前位置:
首页 > 编程开发 > python入门教程 >
  • BeautifulSoup爬取网页分页

在前面我们介绍了如何通过某个页面爬取与之关联的外部网页,当时介绍的是使用广度优先搜索的方式爬取。

在本节,我们将介绍另一种爬取外部链接的方式,即深度优先搜索,爬取网页的分页。

由于本人喜欢古诗词,今天爬取的网页的内容就是古诗词,爬取的链接为:https://so.gushiwen.org/shiwen/。

如下图所示:

在同一个网页,内容是通过分页的形式进行展示,今天介绍如何爬取分页。

一、思路分析

我们知道,对于网页的分页内容访问,我们通常可以通过点击“上一页”或者“下一页”按钮访问关联的页面。

因此,在爬取分页内容的时候,我们可以采用这一思路,进行深度递归,即可访问所有的分页内容。

当然,事实上,由于软件工程师在编码过程中会对不同分页的url做一定的映射,因此除了以上思路,我们可以选择另外一种解决办法就是分析这种映射关系是什么。

如果能确定映射关系,那么就不需要进行深度递归,毕竟操作系统对于编程语言的栈的深度是有限制的,比如python最多递归1000次,超过1000次将报栈溢出的错误。

在接下来的示例演示中将对两种方法进行介绍。

二、示例演示

1.深度递归访问分页内容

正如之前一直强调的,在使用网络爬虫进行网页爬取的时候,第一步总是先打开目标网页,然后在开发者模式下分析网页的特点,然后根据分析的结果进行爬取。

打开目标网页的结构如下图所示:

在网页中搜索“下一页”按钮所在的标签位置,我们可以看到如上的结果。并且通过认真观察,我们能够看见“下一页”所在的标签是一个超链接,因此我们可以猜测,这个链接就是用来从当前页跳转到下一页。

为了验证我们的猜想是否正确,我们可以直接点开“下一页”按钮的跳转页面是否与我们刚才观察的url一致。

打开“下一页”的页面,我们可以看到url如下:

显然,两个页面是存在联系的。浏览器中的url和超链接的url除了隔了域名https://so.gushiwen.org/不一样之外,后半部分都是一致的。

因此证实了我们的猜想是正确的,因此我们只需要获取页面中“下一页”中超链接的url,再拼接上域名,就能得到下一页页面的url。通过递归操作,我们能够爬取所有的页面。

这里定义一个函数进行实现,使用一个队列存储每次获取的url:

def getNextUrl(url,que):
    '''

    :return:
    '''
    if url==None or len(url)==0:
        return
    try:
        html = requests.get(url)
        html.encoding = None
        new_url = bs.findAll("a", {"class": "amore"})[0].attrs['href']
        new_url= domain_prefix+new_url
        que.put(new_url)
        getNextUrl(new_url,que)
    except Exception as e:
        print("get attr error!")

在找到不同分页页面间的关联关系之后,接下来就是找到我们的目标内容:古诗词所在的位置。通过我们前面介绍的知识,我们很容易找到我们的目标内容:

利用我们上一章的讲过的标签定位方法,很容易就可以获取到目标内容。

为了方便查看,我们把每首诗的内容使用文件进行存储,代码实现如下:

def get_poetry(bs):
    '''
    获取每首诗的内容

    :param url:
    :return:
    '''

    # 将所有诗词内容写入文本
    poetry_set = bs.findAll("div", {"class": "sons"})
    for poetry in poetry_set:
        titles = poetry.findAll('b')
        if len(titles)==0:
            break

        title=titles[0].text.split('/')[-1].strip()
        res=''+title
        with open('./book/'+title+".txt", 'w') as fo:
            info = poetry.findAll('p', {'class': 'source'})
            for i in info:
                fo.write(i.get_text())
                res+=','+i.get_text()

            content = poetry.findAll('div', {'class': 'contson'})
            for i in content:
                fo.write(i.get_text())
                res += ','+i.get_text()

为了提高执行效率,我们可以把页面内容提取和url访问分开,使用多线程进行并行操作。同时,要考虑到访问过程中会存在访问出错的情况,因此需要捕获异常。

最终得到的完整代码如下:

# 请求库
import requests
# 解析库
from bs4 import BeautifulSoup
from queue import Queue
import threading
import os
import re

domain_prefix=r"https://so.gushiwen.org"

def get_poetry(bs):
    '''
    获取每首诗的内容

    :param url:
    :return:
    '''

    # 将所有诗词内容写入文本
    poetry_set = bs.findAll("div", {"class": "sons"})
    for poetry in poetry_set:
        titles = poetry.findAll('b')
        if len(titles)==0:
            break

        title=titles[0].text.split('/')[-1].strip()
        res=''+title
        with open('./book/'+title+".txt", 'w') as fo:
            info = poetry.findAll('p', {'class': 'source'})
            for i in info:
                fo.write(i.get_text())
                res+=','+i.get_text()

            content = poetry.findAll('div', {'class': 'contson'})
            for i in content:
                fo.write(i.get_text())
                res += ','+i.get_text()

def getNextUrl(url,que):
    '''

    :return:
    '''
    if url==None or len(url)==0:
        return

    try:
        html = requests.get(url)
        html.encoding = None
        bs = BeautifulSoup(html.text, 'html.parser')
        #启动一个子线程进行内容处理
        t = threading.Thread(target=get_poetry, args=(bs,))
        t.start()
        t.join()

        new_url = bs.findAll("a", {"class": "amore"})[0].attrs['href']
        new_url= domain_prefix+new_url
        que.put(new_url)
        getNextUrl(new_url,que)
    except Exception as e:
        print("get attr error!")

爬取的网页链接

url=r"https://so.gushiwen.org/shiwen/"
html=requests.get(url)
html.encoding=None
bs=BeautifulSoup(html.text,'html.parser')

#爬取所有的网页链接
urls=Queue()
getNextUrl(url,urls)

最终得到的结果如下:

2.研究映射关系快速访问

通过递归的方式获取所有的url思路很清晰,也很简单,但是效率不高。因此,为了提高效率,我们可以比较分析不同的特点,找出规律。

摘取部分url展示如下:

通过细细比较,我们可以很容易发现,这些url除了最后的1位或者两位数字不同之外,其他的都是相同的,并且这些数字存在一个以1为步长的线性增长关系。

因此我们完全可以通过一个循环来生成所有的url,代码实现如下:

#所有的网页链接
urls=Queue()
for i in range(1,101,1):
    tmp=url.replace("1.aspx",str(i)+".aspx")
    print(tmp)
    urls.put(tmp)

在得到所有的URL之后,接下来我们只需要抓取每个页面的url的HTML页面,然后获取内容皆可。除了获取url的方式不同之外,其他抓取的方式与深度递归方式差不多,如下所示:

def get_poetry(que):
    '''
    获取每首诗的内容

    :param url:
    :return:
    '''
    while not que.empty():
        url=que.get()

        try:
            html = requests.get(url)
            html.encoding = None
            bs = BeautifulSoup(html.text, 'html.parser')
            # 将所有诗词内容写入文本
            poetry_set = bs.findAll("div", {"class": "sons"})
            for poetry in poetry_set:
                titles = poetry.findAll('b')
                if len(titles)==0:
                    break

                title=titles[0].text.split('/')[-1].strip()
                print(title)
                res=''+title
                with open('./book/'+title+".txt", 'w') as fo:
                    info = poetry.findAll('p', {'class': 'source'})
                    for i in info:
                        fo.write(i.get_text())
                        res+=','+i.get_text()

                    content = poetry.findAll('div', {'class': 'contson'})
                    for i in content:
                        fo.write(i.get_text())
                        res += ','+i.get_text()
                    
        except Exception as e:
            print(url)
            print("get attr error!")

因为抓取的页面之间不存在先后关系,因此可以并行执行。这里采用多线程的方式来提高抓取的效率。

得到最终的代码如下:

# 请求库
import requests
# 解析库
from bs4 import BeautifulSoup
from queue import Queue
import threading
import pyttsx3
import os
import re

domain_prefix=r"https://so.gushiwen.org"

def get_poetry(que):
    '''
    获取每首诗的内容

    :param url:
    :return:
    '''
    while not que.empty():
        url=que.get()

        try:
            html = requests.get(url)
            html.encoding = None
            bs = BeautifulSoup(html.text, 'html.parser')
            # 将所有诗词内容写入文本
            poetry_set = bs.findAll("div", {"class": "sons"})
            for poetry in poetry_set:
                titles = poetry.findAll('b')
                if len(titles)==0:
                    break

                title=titles[0].text.split('/')[-1].strip()
                print(title)
                res=''+title
                with open('./book/'+title+".txt", 'w') as fo:
                    info = poetry.findAll('p', {'class': 'source'})
                    for i in info:
                        fo.write(i.get_text())
                        res+=','+i.get_text()

                    content = poetry.findAll('div', {'class': 'contson'})
                    for i in content:
                        fo.write(i.get_text())
                        res += ','+i.get_text()

        except Exception as e:
            print(url)
            print("get attr error!")

# 爬取的网页链接
url=r"https://so.gushiwen.org/shiwen/default_2A515ea88d1858A1.aspx"

#爬取所有的网页链接
urls=Queue()
# urls.put(url)
for i in range(1,101,1):
    tmp=url.replace("1.aspx",str(i)+".aspx")
    print(tmp)
    urls.put(tmp)

# 定义多线程
for i in range(10):
    t = threading.Thread(target=get_poetry, args=(urls,))
    t.start()
    t.join()

通过执行对比分析,我们很容易就可以发现第二种方法比第一种方法不仅代码更为简洁,效率也更高。

当然,抓取内容只是整个数据分析的基本操作,为了对数据进行更深入的理解,我们需要借助一些数据分析的工具对数据进行处理和分析。

由于涉及的内容较多,难度也更大,将在后续的篇章逐渐给大家进行介绍。

出处:

https://www.cnblogs.com/turing09/p/13171858.html

相关教程