一、常见的Python爬虫库
1. Requests:处理Http请求。
2. lxml:HTML和XML文件解。
3. BeautifulSoup:网络抓取框架,用于解析和提取HTML和XML数据,通常用于小数据量的处理,不支持异步操作,通常搭配lxml使用。
4. Scrapy:比较强大的爬虫框架,适用于复杂大型任务的爬取。
5. Selenium:模拟用户访问浏览器操作,适合处理JS渲染的网页。
6. re:正则表达式。
二、爬虫示例 1
示例描述: 爬取自己的csdn博客,统计每篇博客的访问量,制作一个柱状图,以访问量从大到小的方式显示。
1. 从“个人主页”爬取所有所有文章的链接
1.1 查看爬取规则
打开个人主页,右键->检查:可以看到每篇文章的链接挂在哪个标签的哪个属性下( <article>标签下的<a>标签中的href属性值即为每篇文章的链接 )
1.2 提取网页中的所有文章ip
from bs4 import BeautifulSoup #pip3 install beautifulsoup4
from urllib.request import urlopen
homePage_url="https://blog.csdn.net/beautiful77moon?type=blog" #你的csdn个人主页链接
homePage_html=urlopen(homePage_url).read().decode('utf-8')
soup=BeautifulSoup(homePage_html,features='lxml')
#1.查找所有的<article>标签
li_articles=soup.find_all('article')
#2.取出所有<article>标签下<a>中的href属性值
article_urls=[]
for item in li_articles:
link=item.find_all('a')
article_urls.append(link[0]['href'])
print(link[0]['href'])
当页面内容过多时,需要下拉"加载",才能显示所有内容,所以需要一个工具模拟浏览器行为,自动滚动页面以加载更多内容。urllib无法处理这种情况,所以一般不建议使用 urllib。
1.3 使用selenium模拟浏览器。
1.3.1 下载浏览器驱动(以Edge为例)
1. 查看自己的浏览器版本(点击浏览器右上角的三个点->设置->关于 Microsoft Edge)
2. 下载对应版本的驱动:Microsoft Edge WebDriver | Microsoft Edge Developer
3. 解压到一个目录下(这个目录后续会用到)
1.3.2 下载关键的依赖包
1. 浏览器模拟器selenium:pip install selenium --index-url https://pypi.tuna.tsinghua.edu.cn/simple
2. 处理网页的beautifulsoup:pip install beautifulsoup4 --index-url https://pypi.tuna.tsinghua.edu.cn/simple
1.3.3 代码
1. 模拟浏览器,实现“滑动鼠标”下拉页面以加载更多数据的行为
2. 从个人主页提取所有文章的url并打印
from selenium import webdriver
from selenium.webdriver.edge.service import Service
from selenium.webdriver.edge.options import Options
from bs4 import BeautifulSoup
import time
# 设置 Edge驱动 的路径
edge_driver_path = 'E:\SoftWare_work\download\edgedriver_win64\msedgedriver.exe' # 替换为你本地的 EdgeDriver 路径
# 配置 Edge浏览器选项
edge_options = Options()
edge_options.add_argument("--headless") # # 无头模式。不可视化浏览器页面。如果注释掉这行,每次运行代码都会打开浏览器页面。
# 启动浏览器
service = Service(edge_driver_path)
driver = webdriver.Edge(service=service, options=edge_options)
# 打开网页
homePage_url = "https://blog.csdn.net/beautiful77moon?type=blog"
driver.get(homePage_url)
# 滚动页面以加载更多内容
# driver.execute_script("return window.pageYOffset + window.innerHeight") 用于获取当前视口底部相较于页面顶部位置的 JavaScript 代码
# window.pageYOffset:当前视口顶部相对于页面顶部的垂直滚动距离。表示页面的顶部已经滚动了多少像素 假设为1200px
# window.innerHeight:浏览器视口的高度(即当前显示区域的高度) 假设为800px
# last_height = window.pageYOffset + window.innerHeight = 2000px
last_height = driver.execute_script("return window.pageYOffset + window.innerHeight")
while True:
#driver.execute_script("window.scrollTo(0, document.body.scrollHeight);") #使用 JavaScript 滚动到页面底部以加载更多数据
#模拟鼠标滚动
#第一个参数表示水平方向不滚动
#第二个参数表示垂直方向上滚动1000像素
driver.execute_script(f"window.scrollBy(0, 1000);")
time.sleep(5) #等待页面加载
new_height = driver.execute_script("return window.pageYOffset + window.innerHeight")#driver.execute_script("return document.body.scrollHeight") #获取当前页面的高度,检测是否已加载更多内容
if new_height == last_height:
break
last_height = new_height
# 获取页面源代码
page_source = driver.page_source
# 关闭浏览器
driver.quit()
# 解析页面内容
soup = BeautifulSoup(page_source, 'lxml')
# 查找所有的<article>标签
li_articles = soup.find_all('article')
# 取出所有<article>标签下<a>中的href属性值
article_urls = []
for item in li_articles:
links = item.find_all('a')
if links:
article_urls.append(links[0].get('href'))
print(links[0].get('href'))
print(len(article_urls)) #116
2. 统计每篇文章的访问量
注:该内容可以直接从首页爬取,这里是为了演示爬虫爬取数据的一种逻辑。
2.1 查看爬取规则
参考 1.1
2.2 爬取数据
注意:1.3.3 代码中的 driver.quit() 需要注释掉,否则不能正常浏览其他网页,会报错
import re
#分析每篇文章的访问量,收藏量,点赞数
class Article_Detail:
def __init__(self,article_name="",read="",collect="",like=""):
self.article_name=article_name #文章名字
self.read=read #阅读量
self.collect=collect #收藏量
self.like=like #点赞量
articles_details=[]
for item in article_urls:
driver.get(item)
page_source=driver.page_source
#driver.quit()
soup = BeautifulSoup(page_source,'lxml')
# 阅读量、收藏量、点赞数所在标签过滤
bar_content=Article_Detail()
get_name=(soup.find('h1',attrs='title-article',id='articleContentId')).get_text()
get_read=(soup.find('span','read-count')).get_text(strip=True) #strip=True去除前后空格
get_collection=(soup.find('span','get-collection')).get_text(strip=True)
get_like=(soup.find('span',id='blog-digg-num')).get_text(strip=True)
#统计文章的阅读量、收藏量、点赞数
bar_content.article_name=get_name
match=re.search(r'\d+',get_read) #使用正则表达式匹配字符串中的一个或多个数字
bar_content.read= match.group() if match else 0
match = re.search(r'\d+', get_collection) # 使用正则表达式匹配字符串中的一个或多个数字
bar_content.collect = match.group() if match else 0
match = re.search(r'\d+', get_like) # 使用正则表达式匹配字符串中的一个或多个数字
bar_content.like = match.group() if match else 0
articles_details.append(bar_content)
for item in articles_details:
print(item.article_name+":阅读量["+item.read+"],收藏量["+item.collect+"],点赞数["+item.like+"]")
运行结果:
三、爬虫框架Scrapy
使用Scrapy框架实现"示例1"中的功能,并将爬取的结果进行保存。
1. Scrapy框架与BeautifulSoup框架的区别:
[功能方面]
- Scrapy:
- 数据类型:可以处理各种类型的数据。
- 功能方面:包括请求管理、数据解析/提取、数据存储(JSON、CSV、XML)等。
- 请求方式:使用异步 I/O,使其能够高效地处理大量请求。
- 支持的特性:支持中间件、管道、爬虫调度等功能,适合大规模的抓取项目。
- 自动处理:能够自动处理重定向、用户代理、更换IP等。
- BeautifulSoup:
- 数据类型:是一个用于解析 HTML 和 XML 的库,主要用于提取网页内容。
- 功能方面:只解析和提取数据,不包括请求管理和数据存储。
- 请求方式:同步I/O操作,通常与requests等库配合使用。
[性能方面]
- Scrapy:
- 性能高:因其异步I/O和内置的请求调度功能,适合大规模抓取。
- 扩展性强:可以通过中间件和扩展进行功能扩展,如处理JavaScript渲染内容等。
- BeautifulSoup:
- 性能较低:通常用于小规模数据抓取,不支持异步操作。
- 扩展性有限,主要集中与数据解析,需要与其他工具组合使用。
[适用场景]
- Scrapy:
- 大规模爬取:适合需要高效处理大量数据的项目。
- 复杂任务抓取:适用于抓取并处理多个页面、分页、异步请求等复杂任务。
- BeautifulSoup:
- 小型抓取:适合简单的数据提取任务。
- 数据解析:已经知道了html内容,只需要解析和提取数据时。
2. Scrapy框架使用步骤
1. 下载相应的依赖包:pip install scrapy
2. 命令行创建一个爬虫项目:scrapy startproject csdn_blog
2. 创建爬虫:
①进入爬虫目录:cd csdn_blog
② 执行命令:scrapy genspider csdn_spider https://blog.csdn.net/beautiful77moon?type=blog
4. 打开items.py定义item容器
import scrapy
class CsdnBlogItem(scrapy.Item):
# define the fields for your item here like:
title=scrapy.Field()
read=scrapy.Field()
collect=scrapy.Field()
like=scrapy.Field()
5. 进入 csdn_spider.py编写爬虫代码(不包含模拟浏览器的功能)
注:文章的数据内容可以直接从首页爬取,为了演示爬虫爬取数据的逻辑,这里对抓取的所有ip都进行了爬取。 实际应用中,怎么简单怎么处理。
import re
import scrapy
class CsdnSpiderSpider(scrapy.Spider):
name = "csdn_spider"
allowed_domains = ["blog.csdn.net"]
start_urls = ["https://blog.csdn.net/beautiful77moon?type=blog"]
def parse(self, response):
#提取文章链接
article_urls=response.css('article a::attr(href)').getall() # ::attr(href) 提取href属性的值
article_urls=[url for url in article_urls if url.startswith("https://")]
#分析所有链接
for url in article_urls:
yield scrapy.Request(url,callback=self.parse_article)
#提取每篇文章的数据
def parse_article(self,response):
title=response.css('h1.title-article::text').get(default='N/A').strip() #::text 提取文本内容
read=self.extract_number(response.css('span.read-count::text').get(default='0').strip()) #
collect=self.extract_number(response.css('span.get-collection::text').get(default='0').strip()) # '.':class
like=self.extract_number(response.css('span#blog-digg-num::text').get(default='0').strip()) # '#':id
yield {
'title':title,
'read' : read,
'collect' : collect,
'like' : like
}
#正则化
def extract_number(self, text):
match = re.search(r"\d+(\.\d+)?k?", text) #对于1200这种数据会显示为1.2k
number=match.group() if match else '0'
if 'k' in number:
number=number.replace('k','')
number=float(number)*1000
else:
number=int(number)
return int(number)
6. 运行: scrapy crawl csdn_spider -o articles.json (将爬取的内容输出到articles.json)
注意: 运行时会报错:Forbidden by robots.txt: <GET https://blog.csdn.net/beautiful77moon?type=blog>
网站的robots.txt文件阻止了 Scrapy 访问该页面,可以修改settings.py中的ROBOTSTXT_OBEY = False配置:来忽略robots.txt规则。(谨慎使用)
8. 运行结果如下:
四、使用Jupyter对结果进行可视化
1. 安装所需要的包
pip install notebook --index-url https://pypi.tuna.tsinghua.edu.cn/simple
pip install pandas --index-url https://pypi.tuna.tsinghua.edu.cn/simple
pip install matplotlib --index-url https://pypi.tuna.tsinghua.edu.cn/simple
pip install seaborn --index-url https://pypi.tuna.tsinghua.edu.cn/simple
2. 启用jupyter notebook
1. 在pycharm中运行命令,打开一个网页: jupyter notebook
2. 新建notebook
3. 运行代码
3. 对爬取的数据可视化
绘制直方图和曲线图显示爬取的所有数据:
1. X轴:每篇文章的标题
2. Y轴:直方图的Y轴数据为所有数据的和,三条曲线图的Y轴数据分别为博客的阅读量、收藏量和点赞量。
import pandas as pd
import matplotlib.pyplot as plt
import json
# 读取JSON文件并加载数据
with open('article.json', 'r', encoding='utf-8') as file:
data = json.load(file)
# 将数据转换为DataFrame
df = pd.DataFrame(data)
# 检查是否有缺失值并处理(例如填充缺失值或删除含缺失值的行)
df = df.dropna(subset=['read', 'collect', 'like'])
X=df["title"]
Y_read=df["read"]
Y_collect=df["collect"]
Y_like=df["like"]
Y_sum=Y_read+Y_collect+Y_like
# 配置字体 不配置字体可能会出现中文乱码
plt.rcParams['font.sans-serif'] = ['SimHei'] # 使用黑体
plt.rcParams['axes.unicode_minus'] = False # 正确显示负号
plt.figure()
plt.bar(X,Y_sum,facecolor='purple',edgecolor='white',label='总数') #直方图
for x,y in zip(X,Y_sum):
plt.text(x,y+50,y,ha="center",va="top")
plt.plot(X,Y_read,color='red',label='阅读量')
for x,y in zip(X,Y_read):
plt.text(x,y+50,y,ha="center",va="top",color='red')
plt.plot(X,Y_collect,color='green',label='收藏量')
for x,y in zip(X,Y_collect):
plt.text(x,y+50,y,ha="center",va="top",color='green')
plt.plot(X,Y_like,color='blue',label='点赞量')
for x,y in zip(X,Y_like):
plt.text(x,y+50,y,ha="center",va="top",color='blue')
#添加图例
plt.legend()
#旋转x轴标签
plt.xticks(rotation=45,ha='right')
# 显示图形
plt.show()
运行结果: