Python web crawler(1)基本用法格式
用with读取文件
# './素材/匹配天气.html'是文件路径,'r'表示读取模式,encoding='UTF-8'指定编码为UTF-8
with open('../素材/匹配天气.html', 'r', encoding='utf-8') as file:
# 读取文件内容并将其保存在变量data中
data = file.read()
用with写入文件
with open('../练习答案/股票2.html', mode='w', encoding="gbk") as file:
file.write(tbodyData[0])
当前内容的类型如果是标签"bs4.element.Tag"
的话,写入到文件中 注意 需要类型转换为字符串str()
print(type(soup.find('a', class_="cover").img))
with open('save.txt', 'w') as f:
f.write(str(soup.find('a', class_="cover").img))
#执行结果
<class 'bs4.element.Tag'>
进程已结束,退出代码0
使用bs4中的BeautifulSoup库解析HTML内容 ,'lxml'是指定解析器的一种,它是一个快速而灵活的XML和HTML解析库。
from bs4 import BeautifulSoup
with open('./素材/豆瓣.html', 'r', encoding='UTF-8') as f:
data = f.read()
soup = BeautifulSoup(data, 'lxml')
创建CSV文件以写入数据
在使用 open
函数创建 CSV 文件时,newline=''
是一个参数,用于指定在写入文件时如何处理行尾符号。在这里,newline=''
的作用是禁止在行尾插入额外的换行符,以防止在Windows系统上出现不必要的空行。
with open('output.csv', 'w', newline='', encoding='gbk') as csvfile:
csvwriter = csv.writer(csvfile)
获取div标签(只查找第一个div标签)
print(soup.div)
.attrs
获取标签有哪些属性
print(soup.div.attrs) # 查看div都有哪些属性值
# {'id': 'db-global-nav', 'class': ['global-nav']}
print(soup.find('div', attrs={"class": "detail-frame"}))
# find第一个,从<div class="detail-frame">到</div>里面的所有内容
print(soup.div.attrs['id']) # 获取当前div的id属性值
# db-global-nav
print(soup.div.attrs['class']) # 获取当前div的class属性值
# ['global-nav']
.find
查找(find只查找第一个符合的值,不会继续向后查询)
# 查找span标签中,class类为"font-small color-lightgray"的值
print(soup.find('span', class_="font-small color-lightgray"))
# 查找<span>标签中,class类为"fleft gray-link",id值为"icp"的值
print(soup.find('span', class_="gray-link", id="icp"))
这里需要特别注意
- 因为
class
是python中类的关键字,因此需要特殊处理为class_
(一般在html中只有class这个需要特殊处理,别的到目前还没有出现过) class_="fleft gray-link"
这里可以只取"gray-link"
或"fleft"
其中任意一个,也可以都取。这里是且的逻辑关系
执行结果1:
<span class="font-small color-lightgray">评分9.7</span>
执行结果2:
<span id="icp" class="fleft gray-link">
<p>
© 2005-2017 douban.com, all rights reserved 北京豆网科技有限公司
</p>
</span>
.find
和.
组合使用
print(soup.find('a', class_="cover"))
print(soup.find('a', class_="cover").img) # 获取里面的img
print(soup.find('a', class_="cover").img.attrs) # 获取里面的img 属性
print(soup.find('a', class_="cover").img.attrs['src']) # 获取里面的img 属性
print(soup.find('a', class_="cover").img['src']) # 获取里面的img 属性
print(soup.find('a', class_="cover").find('img')) # 获取里面的img 属性
print(soup.find('a', class_="cover").img) # 获取里面的img 属性
print(type(soup.find('a', class_="cover").img)) # 查看类型
.find
一层一层的拿内容
html的内容架构如下:
<div class="article">
<h2>虚构类 · · · · · · </h2>
<ul class="cover-col-4 clearfix">
<li>
<a class="cover" href="https://book.douban.com/subject/27104959/"><img src="https://img3.doubanio.com/mpic/s29535271.jpg"/></a>
<div class="detail-frame">
<h2>
<a href="https://book.douban.com/subject/27104959/">离开的,留下的</a>
</h2>
</div>
</li>
</ul>
通过使用.ul
和.h2
逐一查找数据
print(soup.find("div", class_="article").ul.h2)
使用2次find查找
result = soup.find('ul', class_='cover-col-4 clearfix').find('div', class_='detail-frame').h2.a
print(result)
# 运行结果
<a href="https://book.douban.com/subject/27104959/">离开的,留下的</a>
获取内部的内容,而不是标签.string
、.text
、.get_text()
print(soup.find('h2', class_="fleft").text)
print(soup.find('h2', class_="fleft").get_text()) # 等同于text
print(soup.find('h2', class_="fleft").string) # 如果标签还有嵌套 则为None
print(soup.find('h2', class_="fleft").strings) # 获取当前下所有文本返回生成器,必须要用列表接收
print(list(soup.find('h2', class_="fleft").strings)) # 返回生成器
print(list(soup.find('h2', class_="fleft").stripped_strings)) # 获取当前下所有文本返回生成器,去除空格
# 多层嵌套
print(soup.find('div', class_="article").h2.a.string) # 如果标签还有嵌套 则为None
print(soup.find('div', class_="detail-frame").h2.a.text)
print(soup.find('div', class_="detail-frame").h2.a.get_text()) # 等同于text
# 使用CSS选择器语法:find 方法可以接受CSS选择器作为参数,这使得选择更加灵活。例如,你可以直接使用.detail-frame h2 a来选择嵌套结构的元素。
result = soup.find(".detail-frame h2 a").text
print(result)
使用 repr() 查看文本内容原格式
repr()
是一个内置函数,用于返回一个对象的“官方”字符串表示形式。
print(repr(soup.find('div', class_="article").text))
使用 repr() 主要是为了确保字符串的特殊字符得到正确的表示,以便在需要重新创建对象时,能够准确还原原始的字符串。在打印输出或日志中,使用 repr() 可以更清晰地显示字符串的内容。
举例来说,如果soup.find('div', class_="article").text
的文本内容是"Hello\nWorld"
,那么 repr() 就会返回 "'Hello\nWorld'"
,其中 \n 表示换行符。
select
选择器
print(soup.select('h2'))
print(soup.select('ul[class="cover-col-4 clearfix"]'))
print(soup.select('ul[class="cover-col-4 clearfix"] > li')) # 获取子元素li
print(soup.select('ul[class="cover-col-4 clearfix"] > li > a'))
print(soup.select('ul[class="cover-col-4 clearfix"] > li > a > img')) # 按照层级
print(soup.select('ul[class="cover-col-4 clearfix"] img')) # 找到ul里面所有的子孙img标签
img_list = soup.select('ul[class="cover-col-4 clearfix"] img')
for img in img_list:
# print(img.get('src'))
print(img['src'])
select
选择器能最大化满足你所想获取的数据,请尽量使用此方法获取
result = soup.select('ul.cover-col-4.clearfix div.detail-frame h2 a')
for i in result:
print(i.text)
print(i.attrs['href'])
'''
离开的,留下的
https://book.shu.com/subject/27104959/
利维坦之书
https://book.shu.com/subject/27149647/
吃瓜时代的儿女们
https://book.shu.com/subject/27176647/
废土
https://book.shu.com/subject/27113794/
巫术师
https://book.shu.com/subject/27071334/
'''
-
soup.select('ul.cover-col-4.clearfix div.detail-frame h2 a')
:- 使用
select
方法通过 CSS 选择器语法查找在soup
对象中(HTML文档的BeautifulSoup表示)所有匹配指定选择器的元素。 - 选择器字符串
'ul.cover-col-4.clearfix div.detail-frame h2 a'
表示选择所有具有类名 "cover-col-4 clearfix" 的<ul>
元素中包含有类名 "detail-frame" 的<div>
元素下的<h2>
元素内的所有<a>
元素。
- 使用
-
result = ...
:
-
- 将整个选择器的结果赋值给变量
result
。这个结果是一个元素列表,包含所有匹配指定选择器的元素。
- 将整个选择器的结果赋值给变量
所以,这段代码实际上是在HTML文档中查找所有满足指定CSS选择器条件的元素。在这个例子中,它寻找的是位于具有类名 "cover-col-4 clearfix" 的 <ul>
元素中,包含有类名 "detail-frame" 的 <div>
元素下的 <h2>
元素内的所有 <a>
元素。
定义一个函数 is_valid_td 用于过滤 <td> 元素,确保不包含 rowsPan 类
# 定义一个函数 is_valid_td 用于过滤 <td> 元素,确保不包含 rowsPan 类
def is_valid_td(tag):
return tag.name == 'td' and 'rowsPan' not in tag.get('class', [])
# 使用CSS选择器选取所有表格行
rows = soup.select('div.hanml tr')
# 遍历每个表格行
for row in rows:
# 查找行中的所有单元格并过滤掉含有 rowsPan 类的 <td> 元素
cells = row.find_all(is_valid_td)
-
def is_valid_td(tag):
:这一行定义了一个名为is_valid_td
的函数,该函数以一个 BeautifulSoup 标签对象作为参数(通常被称为tag
)。 -
tag.name == 'td'
:这一部分检查 HTML 标签的名称是否为 'td',表示它是一个表格单元格。tag.name
返回标签的名称。 -
'rowsPan' not in tag.get('class', [])
:这一部分检查字符串 'rowsPan' 是否不在标签的类属性列表中。-
tag.get('class', [])
:这获取标签的 'class' 属性的值。如果 'class' 属性不存在,则返回一个空列表(由默认值[]
指定)。 -
'rowsPan' not in ...
:检查 'rowsPan' 是否不在类列表中。
-
-
return
语句:如果两个条件都满足,表示标签是一个没有 'rowsPan' 类的有效<td>
元素,函数返回True
。如果任何条件不满足,函数返回False
。
总之,is_valid_td
函数对于没有 'rowsPan' 类的 <td>
标签返回 True
,否则返回 False
。这个函数在调用 find_all
时被用作筛选条件,选择只有特定条件的 <td>
元素。
查看代码
from bs4 import BeautifulSoup
import csv
# 打开HTML文件并用BeautifulSoup解析
with open('../素材/匹配天气.html', 'r', encoding='utf-8') as f:
soup = BeautifulSoup(f.read(), 'lxml')
# 使用CSS选择器选取所有表格行
rows = soup.select('div.hanml tr')
# 定义一个函数 is_valid_td 用于过滤 <td> 元素,确保不包含 rowsPan 类
def is_valid_td(tag):
return tag.name == 'td' and 'rowsPan' not in tag.get('class', [])
# 创建一个CSV文件并准备写入数据
with open('output.csv', 'w', newline='', encoding='gbk') as csvfile:
csvwriter = csv.writer(csvfile)
# 遍历每个表格行
for row in rows:
# 查找行中的所有单元格并过滤掉含有 rowsPan 类的 <td> 元素
cells = row.find_all(is_valid_td)
# 构建一个空列表 row_data 用于存储单元格文本内容
row_data = []
# 遍历每个单元格
for cell in cells:
# 获取单元格的文本内容,并去除首尾空白字符
cell_text = cell.get_text(strip=True)
# 将单元格文本内容添加到 row_data 列表中
row_data.append(cell_text)
# 将 row_data 列表写入CSV文件的一行
csvwriter.writerow(row_data)