Day 15:专案02 - PTT C_Chat版爬虫02 | BeautifulSoup

大家安安,欢迎来到铁人赛的第15天! 不知不觉已经过完一半了,再努力坚持下去吧!

昨天已经将网站的原始码抓下来了,然而我们只需要原始码中特定的几笔资料而已,所以今天就来讲如何解析原始码,筛选出我们要的资料。

BeautifulSoup

BeautifulSoup是Python的套件之一,Anaconda预设也已经载好了,如果不是Anaconda环境的人,一样使用pip安装下来。

// CMD
pip install beautifulsoup4

安装完後就可以在程序码中引用了。

from bs4 import BeautifulSoup

接下来要使用BeautifulSoup解析原始码,但因为PTT C_Chat版内容太多了,比较不好讲解,所以我这边用crawl_me.html作为示范,也可以复制到你的电脑跟着一起做。

<!-- crawl_me.html -->
<!DOCTYPE html>
<html>
<body>
    <div class="main">
        <img src="source.jpg" alt="">
        <h1 class="heading">Heading 1</h1>
        <h2 id="this" class="heading">Heading 2-1</h2>
        <h2 class="heading">Heading 2-2</h2>
        <h2 class="heading">Heading 2-3</h2>
        <div class="container">
            <p>This is a paragraph</p>
        </div>
    </div>
</body>
</html>

解析原始码

BeautifulSoup有提供两种解析器,一种是html.parser,另一种是xml,因为现在抓到的是HTML,所以选html.parser

解析原始码後,会返回一个DOM tree的物件,初始位置在文件的root,之後就是对这个物件去操作。

prettify()这个函数可以将DOM tree以比较美观的方式印出。

# 读档
response = ""
with open("crawl_me.html", "r", encoding="utf8") as file:
    response = file.read()

# BeautifulSoup解析原始码
soup = BeautifulSoup(response, "html.parser")
print(soup.prettify())

部分执行结果:

定位节点

原始码解析完後是一个树状的结构,每一个标签都代表了一个节点,我们要先定位到想要的节点後,才能取得他的文字或属性。以下提供四种定位方法

find()

find()函数可以定位符合标签的第一个节点。

h1 = soup.find("h1")
print(h1)
<h1 class="heading">Heading 1</h1>

也可以定位指定的属性值。

使用class属性定位,但因为在Python中已经有class保留字了,所以改用class_

container = soup.find("div", class_="container")
print(container)
<div class="container">
<p>This is a paragraph</p>
</div>

用id属性定位。

this = soup.find("h2", id="this")
print(this)
<h2 class="heading" id="this">Heading 2-1</h2>

find_all()

find_all()定位符合标签的所有节点,回传的是一个列表。

h2s = soup.find_all("h2")
print(h2s)
print(h2s[1])   # 使用索引值
[<h2 class="heading" id="this">Heading 2-1</h2>, <h2 class="heading">Heading 2-2</h2>, <h2 class="heading">Heading 2-3</h2>]
<h2 class="heading">Heading 2-2</h2>

如果想定位多个标签,则将标签打包成一个列表就好了。limit属性则可以限制数量。

h1_h2s = soup.find_all(["h1", "h2"], limit=3)
print(h1_h2s)
print(len(h1_h2s))
[<h1 class="heading">Heading 1</h1>, <h2 class="heading" id="this">Heading 2-1</h2>, <h2 class="heading">Heading 2-2</h2>]
3

select_one()

select_one()使用CSS选择器的语法来定位节点,忘记CSS选择器的人可以到 Day 04 复习一下。

h1 = soup.select_one("h1")
print(h1)

p = soup.select_one("div.container") # class定位
print(p)

this = soup.select_one("h2#this") # id定位
print(this)
<h1 class="heading">Heading 1</h1>

<div class="container">
<p>This is a paragraph</p>
</div>

<h2 class="heading" id="this">Heading 2-1</h2>

结果和find()是一样的。

select()

select()其实就是使用CSS选择器语法的find_all()啦。回传是一个列表。

h2s = soup.select("h2")
print(h2s)
print(h2s[1])
[<h2 class="heading" id="this">Heading 2-1</h2>, <h2 class="heading">Heading 2-2</h2>, <h2 class="heading">Heading 2-3</h2>]
<h2 class="heading">Heading 2-2</h2>

取得文字

定位到指定的节点後,可以使用textstring取得文字,或者也可以用getText()

h1 = soup.find("h1")
print(h1.getText())
print(h1.text)
print(h1.string)
Heading 1
Heading 1
Heading 1

取得属性值

对於有属性值的节点,就用get("属性")或类似字典的方式["属性"]取得属性值。

我要<img>标签中的src属性值:

img = soup.find("img")
print(img["src"])
print(img.get("src"))
source.jpg
source.jpg

PTT C_Chat板爬虫

知道BeautifulSoup如何定位节点和取得文字後,我们就实际来爬爬看PTT C_Chat板每篇文章的标题吧!

目标:PTT C_Chat板的文章标题(红框圈起来的部分)。

首先,我们对文章标题 右键>>检查,右边会跳出开发人员介面显示文章标题在原始码中的位置。

稍微观察一下,我们会发现所有的文章标题都在class="title"的div中。

所以很简单,程序码就这样写:

import requests
from bs4 import BeautifulSoup

response = requests.get("https://www.ptt.cc/bbs/C_Chat/index.html") # 取得C_Chat的HTML原始码
root = BeautifulSoup(response.text, "html.parser")  # 解析原始码

links = root.find_all("div", class_="title")    # 文章标题
for link in links:
    print(link.text.strip()) # strip()用来删除文字前面和後面多余的空白

结果:

第一次爬虫就完成啦~ 是不是很简单呢(≧▽≦)

小结

今天介绍Python解析HTML原始码的套件 -- BeautifulSoup,学了几个定位节点和取得文字的方法,最後现学现卖,爬取PTT C_Chat板的文章标题!

明天我们改成爬PTT八卦板,是不是跟C_Chat板一样简单呢? 嘿嘿...明天就知道罗 ψ(`∇´)ψ


如果喜欢这系列文章麻烦帮我按Like加订阅,你的支持是我创作最大的动力~

本系列文章以及范例程序码都同步更新在GitHub上,後续会持续的更新,如果喜欢也麻烦帮我按个星星吧~

有任何问题或建议,都欢迎在底下留言区提出,还请大家多多指教。


<<:  Day_08 有线网路应用(一)

>>:  Day 6 作业系统

WordPress 点击图片放大效果-Easy FancyBox 外挂教学

当我们在部落格上写文章贴图片的时候,有些图片本身解析度就比较大,例如一张 4000 x 3000 大...

[Day16] - 利用 direflow.io 将 React Component 转换成 Web Component

昨天解说 Vue 如何制作 Web-Component 今天来说明一下 , 那 React 如何制作...

创建App-(老师版)功能

创建App-(老师版)功能 今天来建设老师版的功能,授课时间与教课纪录。今天来弄弄成授课时间的页面,...

【C++】Singly Linked lists

Linked List 指的是一群资料存在於不连续的记忆体空间~ 其中每个节点包含~ 资料 及 指标...

Rails基本介绍(一)一个实体 && Remove Duplicates from Sorted Array && Remove Element

惯例 这篇先解题分享。 怕来不及打完,这样比较好修改...科科 Remove Duplicates ...