Python自学指南 | 怎样把最好用的Python教程爬取下来?

很多Python初学者都是从廖雪峰的Python教程开始的。我也是廖老师教程的忠实读者。今天学到了爬虫,就想把廖老师的教程爬取下来,方便查阅。下面是我爬取这个教程的简单过程。

一个简单的爬虫大概包含下面的4个步骤:
1.获取网页的URL
2.下载网页的HTML文件
3.解析下载到的HTML,提取所需的数据
4.将提取的数据存储起来

首先,看一下如何获取廖老师教程的全部URL。在浏览器中打开教程的首页,查看源文件,发现教程的URL如下图所示:

Python自学指南 | 怎样把最好用的Python教程爬取下来?
Paste_Image.png

从源文件中可以看到,每篇教程都是由两串随机码组成的(是不是随机的我不确定,水平有限不知道这些代码是怎么生成的)。因此,要爬取所有的教程页面,则需要先将各页面的URL提取出来,然后与根url组合,获得完整的URL。观察整个HTML, URL所在的div标签具备唯一的class属性值“x-sidebar-left-content”,就根据这个特征解析首页的代码,获得URL列表。使用BeautifulSoup解析获取的各页面的URL和标题。

def bs_parser(html):
    tree = BeautifulSoup(html, 'lxml')
    data = tree.find('div', class_='x-sidebar-left-content').find_all('a')
    urls = []
    titles = []
    grades = []
    for item in data:
        urls.append(item.attrs['href'])
        titles.append(item.get_text())
return urls, titles

接下来,将获得的URL与根URL组合,获得完整的URL。使用Python的urllib.request包抓取所有的页面的HTML。

def download(url):
    print("Downloading: %s" % url)
    try:
        result = urllib.request.urlopen(url, timeout=2).read()
    except urllib.error.URLError as e:
        print("Downloading Error:", e.reason)
        result = None
return result

如果要把自己的爬虫伪装成流量器,也可以给其加上首部的信息(当然这里没有必要)。

def download_browser(url, headers):
    opener = urllib.request.build_opener()
    opener.addheaders = headers
    print("Downloading: %s" % url)
    try:
        result = opener.open(url, timeout=2)
        result = result.read()
        print("Download OK!")
    except urllib.request.URLError as e:
        print("Downloading error:", e.reason)
        result = None
return result

第三步就是要解析抓取的HTML文档,提取有用的信息了。和第一步中提取URL的方法类似,先分析页面的代码,确定有用信息的特征,然后用BeautifulSoup将其提取出来。

Python自学指南 | 怎样把最好用的Python教程爬取下来?
Paste_Image.png

内容部分的特征是div标签具备值为“x-wiki-content”的class属性,并且在全文中是唯一的。可以利用该属性来提取数据:

def bs_parser_content(html):
    tree = BeautifulSoup(html, 'lxml')
    data = tree.find('div', class_='x-wiki-content')
    result = data.get_text()
return result

最后,将获取的数据写到文本文件中进行存储。一个简单的爬取大神教程的小爬虫算是做完了。

全部的代码如下:

import urllib, urllib.request, urllib.error
from bs4 import BeautifulSoup
import os

def download(url): # 没有伪装的下载器
    print("Downloading: %s" % url)
    try:
        result = urllib.request.urlopen(url, timeout=2).read()
    except urllib.error.URLError as e:
        print("Downloading Error:", e.reason)
        result = None
    return result

def download_browser(url, headers): # 带浏览器伪装的下载器
    opener = urllib.request.build_opener()
    opener.addheaders = headers
    print("Downloading: %s" % url)
    try:
        result = opener.open(url, timeout=2)
        result = result.read()
        print("Download OK!")
    except urllib.request.URLError as e:
        print("Downloading error:", e.reason)
        result = None
    return result

# 解析首页,获取url
def bs_parser(html):
    tree = BeautifulSoup(html, 'lxml')
    data = tree.find('div', class_='x-sidebar-left-content').find_all('a')
    print(data[0].attrs['href'])
    urls = []
    titles = []
    grades = []
    for item in data:
        urls.append(item.attrs['href'])
        titles.append(item.get_text())
    return urls, titles 

# 解析页面内容
def bs_parser_content(html):
    tree = BeautifulSoup(html, 'lxml')
    data = tree.find('div', class_='x-wiki-content')
    # print(data)
    result = data.get_text()
    return result

# 首页url
url = 'http://www.liaoxuefeng.com/wiki/0014316089557264a6b348958f449949df42a6d3a2e542c000'
root = 'http://www.liaoxuefeng.com'

# header一定是一个元组列表
headers = [
    ('Connection', 'Keep-Alive'),
    ('Accept', 'text/html, application/xhtml+xml, */*'),
    ('Accept-Language', 'en-US,en;q=0.8,zh-Hans-CN;q=0.5,zh-Hans;q=0.3'),
    ('User-Agent', 'Mozilla/5.0 (Windows NT 6.3; WOW64; Trident/7.0; rv:11.0) like Gecko')
]
html = download_browser(url, headers) # 下载首页的HTML
urls, titles = bs_parser(html) # 解析首页的HTML,返回URL和标题
i = 0
for item, title in zip(urls, titles):
    i += 1
    url = root + item
    html = download_browser(url, headers) # 下载页面html
result = bs_parser_content(html) # 解析html,获取数据
# 合成文本文件路径
    fileName = str(i) + ' ' + title.replace(r'/', ' ') + '.txt'
fileName = os.path.join('Results/', fileName)
# 将数据写入到文本文件
    with open(fileName, 'w') as f:
        f.write(result)

相关新闻

历经多年发展,已成为国内好评如潮的Linux云计算运维、SRE、Devops、网络安全、云原生、Go、Python开发专业人才培训机构!