
本教程详细介绍了如何使用python的`requests`、`beautifulsoup`和`pandas`库,从网页中抓取结构化数据,并将其按行解析成清晰的字段(如章节标题、节号和节内容)。文章将指导读者如何利用css选择器精准定位目标元素,并通过迭代和数据重构,最终将抓取到的数据高效地导出为结构化的pandas dataframe,以便进一步分析或保存为csv文件。
在进行网络数据抓取(Web Scraping)时,一个常见的挑战是将从网页上获取的原始文本内容,按照其内在的逻辑结构进行拆分和整理,最终以结构化的形式(例如,按行、按字段)存储起来。本教程将以抓取在线圣经章节为例,演示如何利用Python的requests库获取网页内容,BeautifulSoup库解析HTML,以及pandas库进行数据结构化和导出。
1. 环境准备与库导入
首先,确保您已安装所需的Python库:requests用于发送HTTP请求,BeautifulSoup用于解析HTML,pandas用于数据处理。
import requests from bs4 import BeautifulSoup import pandas as pd import time # 用于添加请求延迟,避免对网站造成过大压力
2. 配置请求头与获取网页内容
为了模拟浏览器访问,我们需要设置User-Agent请求头。然后定义一个函数来获取指定URL的HTML内容。
# 配置请求头,模拟浏览器访问
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36'
}
# 定义获取网页内容的函数
def get_html_content(url):
try:
r = requests.get(url, headers=headers)
r.raise_for_status() # 检查HTTP请求是否成功
return r.text
except requests.exceptions.RequestException as e:
print(f"请求失败: {e}")
return None
# 目标URL
target_url = "https://www.bibliaonline.com.br/ara/mt/1"
html_content = get_html_content(target_url)
if html_content:
# 使用BeautifulSoup解析HTML内容
soup = BeautifulSoup(html_content, 'html.parser')
else:
print("未能获取网页内容,程序退出。")
exit()3. 精准定位与提取数据
这是本教程的核心部分。原始代码尝试获取整个div.verseByVerse,然后在其内部寻找span.bv、span.v和span.t。这种方法的问题在于,find或find_all在循环外部调用时,会获取页面上的第一个匹配项,而不是当前迭代元素内部的匹配项。此外,对整个div.verseByVerse进行get_text()会将其所有子元素的文本拼接在一起,无法实现逐行导出。
立即学习“Python免费学习笔记(深入)”;
正确的做法是,首先定位到每个独立的经文文本(span.t),然后利用其相对位置查找对应的经文编号(span.v)和章节标题(div.bv)。
通过分析目标网页的HTML结构,我们可以观察到:
- 每段经文文本位于标签内。
- 每段经文编号位于其紧邻的标签内。
- 章节标题(如“A genealogia de Jesus Cristo”)位于标签内,且在相关经文的
标签之前。
我们可以使用CSS选择器div.verseByVerse p span.t来精确选择所有经文文本元素。然后,在遍历这些元素时,使用find_previous()方法来查找其前一个兄弟元素或祖先元素,以获取经文编号和章节标题。
# 使用CSS选择器定位所有经文文本(span.t) # 这些span.t通常位于p标签内,p标签又位于div.verseByVerse内 verse_text_elements = soup.select('div.verseByVerse p span.t') # 用于存储提取到的数据 data = [] current_chapter_title = "" for verse_element in verse_text_elements: # 查找最近的、前一个的div元素作为章节标题 # 注意:这里需要考虑标题可能只出现一次,然后适用于其后的多节经文 # 因此,我们应该找到最近的div.bv,并更新current_chapter_title previous_div = verse_element.find_previous('div', class_="bv") if previous_div: current_chapter_title = previous_div.get_text(strip=True) # 查找最近的、前一个的span元素作为经文编号 verse_number_element = verse_element.find_previous('span', class_="v") verse_number = verse_number_element.get_text(strip=True) if verse_number_element else "" verse_content = verse_element.get_text(strip=True) data.append({ '章节标题': current_chapter_title, '经文编号': verse_number, '经文内容': verse_content }) # 打印部分提取结果,以便检查 # for item in data[:5]: # print(item)关键点解释:
- soup.select('div.verseByVerse p span.t'): 这是一个强大的CSS选择器,它会找到所有在class="verseByVerse"的div内部,且在p标签内部的class="t"的span标签。这确保我们只获取到实际的经文文本。
- verse_element.find_previous('div', class_="bv"): 对于当前迭代的verse_element (即span.t),此方法会向上(DOM树方向)查找其所有祖先和前一个兄弟节点,直到找到第一个匹配div标签且class为bv的元素。这样可以动态地获取每个经文所属的章节标题。由于章节标题通常在多个经文前只出现一次,我们使用current_chapter_title变量来存储并复用,直到遇到下一个标题。
- verse_element.find_previous('span', class_="v"): 同样地,这会找到当前经文文本span.t之前最近的span标签,且class为v,即经文编号。
4. 数据结构化与导出
将提取到的数据列表转换为Pandas DataFrame,然后可以方便地导出为CSV文件。
# 将数据转换为Pandas DataFrame df = pd.DataFrame(data) # 打印DataFrame的前几行 print("DataFrame预览:") print(df.head()) # 添加延迟,避免短时间内大量请求 time.sleep(1) # 将数据导出到CSV文件 # index=False表示不将DataFrame的索引写入CSV文件 output_filename = 'biblia_mateus_cap1.csv' df.to_csv(output_filename, index=False, encoding='utf-8-sig') # 使用'utf-8-sig'处理中文编码问题 print(f"\n数据已成功导出到 {output_filename}")5. 完整代码示例
import requests from bs4 import BeautifulSoup import pandas as pd import time # 配置请求头,模拟浏览器访问 headers = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36' } # 定义获取网页内容的函数 def get_html_content(url): try: r = requests.get(url, headers=headers) r.raise_for_status() # 检查HTTP请求是否成功 return r.text except requests.exceptions.RequestException as e: print(f"请求失败: {e}") return None # 目标URL target_url = "https://www.bibliaonline.com.br/ara/mt/1" print(f"正在获取网页内容: {target_url}") html_content = get_html_content(target_url) if html_content: # 使用BeautifulSoup解析HTML内容 soup = BeautifulSoup(html_content, 'html.parser') # 使用CSS选择器定位所有经文文本(span.t) verse_text_elements = soup.select('div.verseByVerse p span.t') # 用于存储提取到的数据 data = [] current_chapter_title = "" # 用于存储当前章节标题,因为标题可能只出现一次 print("开始解析经文数据...") for verse_element in verse_text_elements: # 查找最近的、前一个的div元素作为章节标题 # 如果找到新的章节标题,则更新current_chapter_title previous_div = verse_element.find_previous('div', class_="bv") if previous_div: current_chapter_title = previous_div.get_text(strip=True) # 查找最近的、前一个的span元素作为经文编号 verse_number_element = verse_element.find_previous('span', class_="v") verse_number = verse_number_element.get_text(strip=True) if verse_number_element else "" verse_content = verse_element.get_text(strip=True) data.append({ '章节标题': current_chapter_title, '经文编号': verse_number, '经文内容': verse_content }) # 将数据转换为Pandas DataFrame df = pd.DataFrame(data) # 打印DataFrame的前几行 print("\nDataFrame预览:") print(df.head()) # 添加延迟,避免短时间内大量请求 time.sleep(1) # 将数据导出到CSV文件 output_filename = 'biblia_mateus_cap1.csv' df.to_csv(output_filename, index=False, encoding='utf-8-sig') # 使用'utf-8-sig'处理中文编码问题 print(f"\n数据已成功导出到 {output_filename}") else: print("未能获取网页内容,程序退出。")6. 注意事项与总结
- 网站结构变化: 网页的HTML结构可能会随时间变化。如果您的代码突然失效,请检查目标网站的HTML结构是否已更新,并相应调整您的CSS选择器或find方法。
- 反爬机制: 某些网站有严格的反爬机制,可能会检测并阻止频繁的请求。除了User-Agent,可能还需要考虑使用代理IP、设置更长的请求间隔、处理验证码等。
- robots.txt: 在抓取任何网站之前,请务必查看其robots.txt文件(例如:https://www.bibliaonline.com.br/robots.txt),了解网站的抓取政策。遵守这些规定是网络爬虫的基本道德和法律要求。
- 数据清洗: 抓取到的原始数据可能需要进一步的清洗和格式化,例如去除多余的空格、特殊字符等。
- 错误处理: 在实际项目中,应增加更完善的错误处理机制,例如try-except块来捕获网络请求失败、HTML解析错误等异常。
通过本教程,您应该已经掌握了如何利用Python和相关库,高效地从复杂网页中提取结构化数据,并将其整理成易于分析和存储的格式。这种方法不仅适用于圣经章节,也可以推广到其他需要逐行解析和结构化处理的网页抓取任务中。










