Python爬虫入门教程,使用BeautifulSoup爬取网站信息
前言
笔者也是爬虫小白,平时主要是做Java开发的,只有一些python基础,最近两天想帮朋友写一个简单的爬虫程序,写这篇文章记录一下自己的学习过程。相信对于之前没有爬虫经验的小白来说,有一定的参考价值。
1.准备阶段
再进行程序编写前,我们首先需要准备python环境,还有以下几个版本包
from bs4 import BeautifulSoup # 网页解析,获取数据
import xlwt # 进行excel操作
import requests # 发送请求
import json
import time # 用来暂停的(限制访问频率)
其中json和time是python自带的,其余三个包需要自己安装,可以通过在控制台输入以下命令进行安装(-i 后面的命令表示使用国内镜像安装,使用默认安装可能会因为网络问题导致安装失败)
pip install Beautifulsoup4 -i https://pypi.tuna.tsinghua.edu.cn/simple
pip install requests -i https://pypi.tuna.tsinghua.edu.cn/simple
pip install xlwt -i https://pypi.tuna.tsinghua.edu.cn/simple
2.实现思路
爬虫其实就是首先需要确定自己想爬的内容是在哪个页面中,然后根据URL去获得这个页面的HTML,我们需要的数据一般都在html的标签中,通过解析HTML页面拿到自己需要的内容就可以了。
本次我想爬取的内容是在数据列表的详情信息页中
想要知道详情页面的URL的话,我们首先在浏览器(我用的是edge,谷歌也是类似的)中,按F12(有的系统可能需要Fn+F12)进入开发者页面,然后点击网络
,先点击左上角清空一下当前日志,方便一会点击查看详情
后找到我们的URL请求。
这时候点击查看按钮,我们可以看到本次请求的请求信息和响应信息
在多点击几个详情页面后,我们会发现它们的请求URL只有id=xxxxx
是有区别的,那我们只需要拿到这些id,写个for循环就可以访问所有的详情页面了。
这时候我们在返回列表页面,通过查看请求列表数据的请求体和响应报文,可以发现列表每条数据的信息都在响应中返回了。
它的报文结构大概是这样,其中的id就是我们需要的。
{
"code": 0,
"count": 584,
"data":[
{
"id": 994171,
"time": "2023-12-31 12:30:32",
"first_time": "2023-12-31 12:40:41",
"dial_time": null,
"next_time": "",
"dial_duration": null,
"numberin": "DRSSMZ2312310023",
"big_case": 1,
"grade": 4,
"type": 1,
"sinvalid": 1,
"status": 1,
"classify": 3,
"name_y": "某",
"follow_status": "待跟进",
"dial_type": null,
"types": "人损",
"new_time": "2023-12-31 12:40:41",
"follow_content": "正在跟进",
"contract_num": 0
},
{
"id": 992924,
"time": "2023-12-29 14:52:20",
"first_time": "2023-12-29 14:52:39",
"dial_time": null,
"next_time": "",
"dial_duration": null,
"numberin": "DHYBDAA2312290034",
"big_case": 1,
"grade": 4,
"type": 1,
"sinvalid": 1,
"status": 1,
"classify": 1,
"name_y": "杨女士",
"follow_status": "纯咨询",
"dial_type": null,
"types": "合同",
"new_time": "2023-12-29 15:05:33",
"follow_content": ",
"contract_num": 0
}
]
}
我是将浏览器中的这段json报文复制粘贴到本地,放到一个文件中,然后使用python读取文件,把id都存到一个list中,,在使用request发送请求的时候,只需要在URL中每次拼接上id就行了。
# 解析json数据
def getIdListData():
with open("data/predata.json", 'r') as f:
data = json.load(f)
data_list = data.get('data')
idList = []
for i in data_list:
idList.append(i.get('id'))
return idList
3.实现过程
我们在使用python写爬虫的时候,一般需要三个东西:URL、请求方式、表头信息(Cookie、User-Agent等),这些都可以在浏览器中拿到
拿到这些之后,我们去详情页面,找到我们需要的数据是在哪个标签中,比如我们想要真实姓名这个信息。我们只需要点击开发者页面后,选择左上角的选择按钮,再点到需要的数据中,就可以看到它在HTML的哪个标签中了。
接着我们在这个标签上点击右键,选择复制selector,这样可以直接复制到它的层级结构
获取电话号码的代码如下
def getData(header,url):
header = header
url = url
res = requests.get(url, headers=header)
# 整个页面的HTML
html_doc = res.text
# soup是<class 'bs4.BeautifulSoup'>类型 可以使用.find()、findAll()等方法
soup = BeautifulSoup(html_doc, 'html.parser')
# 拿到电话号码的标签 参数就是selector中的内容
phone_tag = soup.select(
'body > div.cr-detail-box > div.cr-card-box.flex_one > div.cr-card-body > form > div.cr-play-form-list > div:nth-child(3) > div > div.value-box > input')
# 因为值在标签的value中
phone = phone_tag[0]['value']
获取所有信息的完整代码如下,因为我这次需要一个跟进记录
的信息,这个信息每条数据里它的数量都不一样,因此我需要拿到它的父标签,然后通过循环去遍历拿到里面的信息。
# 爬取数据
def getData(header,url):
header = header
url = url
res = requests.get(url, headers=header)
html_doc = res.text
# soup是<class 'bs4.BeautifulSoup'>类型 可以使用.find()、findAll()等方法
soup = BeautifulSoup(html_doc, 'html.parser')
# print(type(soup))
phone_tag = soup.select(
'body > div.cr-detail-box > div.cr-card-box.flex_one > div.cr-card-body > form > div.cr-play-form-list > div:nth-child(3) > div > div.value-box > input')
# <class 'bs4.element.ResultSet'>
# name_tag[0] 是<class 'bs4.BeautifulSoup'>类型
name_tag = soup.select(
'body > div.cr-detail-box > div.cr-card-box.flex_one > div.cr-card-body > form > div.cr-play-form-list > div:nth-child(5) > div > div.value-box > input')
# print(type(name_tag))
# 跟进记录外层标签
record_tagSource = soup.select(
'#FOLLOW_MODULE > div.cr-laymod-box > div.cr-laymod-body.nofoot.cr-timeline-wrap > div')
children_record = record_tagSource[0]
phone = phone_tag[0]['value']
name = name_tag[0]['value']
names = []
phones = []
# 获取存储姓名的标签集合
names_tag = children_record.findAll(name="span", attrs={"class": "title"})
times_tag = children_record.findAll(name="div", attrs={"class": "time-box"})
content_tag = children_record.findAll(name="div", attrs={"class": "explain-box"})
# 跟进记录姓名
namesRecord = []
# 跟进记录时间
timesRecord = []
# 跟进记录内容
contentRecord = []
# i 是这种格式 <span class="title">王卓翔</span>
# 是bs4.element.Tag 类型,使用i.text获取标签间的内容
# 遍历得到当前跟进记录中的所有姓名
for i in names_tag:
namesRecord.append(i.text)
phones.append(phone)
names.append(name)
for i in times_tag:
timesRecord.append(i.text)
# 获取跟进记录内容
for i in content_tag:
# 因为从网页爬取的跟进记录中存在\n\r空格等字符,需要处理一下 只截取“xxx”中的内容
strT = i.text
# 获取字符串中字符首次出现的位置
startIdx = strT.find('“')
# 获取字符串中字符最后一次出现的位置
endIdx = strT.rfind('”')
strN = strT[startIdx + 1:endIdx]+""
contentRecord.append(strN)
return names,phones,namesRecord,timesRecord,contentRecord
将数据存到excel中的代码
# 将数据存到excel中
'''
:param names: 姓名列表
:param phones: 电话列表
:param namesRecord: 跟进人姓名列表
:param timesRecord: 跟进时间列表
:param contentRecord: 跟进内容列表
:param filename: 导出文件名称
:return: null
'''
def outPutExcel(names,phones,namesRecord,timesRecord,contentRecord,filename):
work_book = xlwt.Workbook(encoding='UTF-8')
worksheet = work_book.add_sheet('跟进记录')
size = len(names)
for i in range(1,size+1):
worksheet.write(i,1,names[i-1])
worksheet.write(i,2,phones[i-1])
worksheet.write(i,3,namesRecord[i-1])
worksheet.write(i,4,timesRecord[i-1])
worksheet.write(i,5,contentRecord[i-1])
work_book.save(filename)
最后是整个程序的完整代码,我把里面的cookie信息去掉了,大家拿到后可以无法直接运行出结果,主要是参考
from bs4 import BeautifulSoup # 网页解析,获取数据
import xlwt # 进行excel操作
import requests # 发送请求
import json
import time # 用来暂停的
# 解析json数据
def getIdListData():
with open("data/predata.json", 'r') as f:
data = json.load(f)
data_list = data.get('data')
idList = []
for i in data_list:
idList.append(i.get('id'))
return idList
# 爬取数据
def getData(header,url):
header = header
url = url
res = requests.get(url, headers=header)
# 整个页面的HTML
html_doc = res.text
# soup是<class 'bs4.BeautifulSoup'>类型 可以使用.find()、findAll()等方法
soup = BeautifulSoup(html_doc, 'html.parser')
# 拿到电话号码的标签
phone_tag = soup.select(
'body > div.cr-detail-box > div.cr-card-box.flex_one > div.cr-card-body > form > div.cr-play-form-list > div:nth-child(3) > div > div.value-box > input')
# <class 'bs4.element.ResultSet'>
# name_tag[0] 是<class 'bs4.BeautifulSoup'>类型
# 拿到姓名的标签
name_tag = soup.select(
'body > div.cr-detail-box > div.cr-card-box.flex_one > div.cr-card-body > form > div.cr-play-form-list > div:nth-child(5) > div > div.value-box > input')
# print(type(name_tag))
# 跟进记录外层标签(因为跟进记录的数量不是固定的,最内层标签需要遍历获取)
record_tagSource = soup.select(
'#FOLLOW_MODULE > div.cr-laymod-box > div.cr-laymod-body.nofoot.cr-timeline-wrap > div')
children_record = record_tagSource[0]
phone = phone_tag[0]['value']
name = name_tag[0]['value']
names = []
phones = []
# 获取存储姓名的标签集合 跟进label、class名称去获取
names_tag = children_record.findAll(name="span", attrs={"class": "title"})
times_tag = children_record.findAll(name="div", attrs={"class": "time-box"})
content_tag = children_record.findAll(name="div", attrs={"class": "explain-box"})
# 跟进记录姓名
namesRecord = []
# 跟进记录时间
timesRecord = []
# 跟进记录内容
contentRecord = []
# i 是这种格式 <span class="title">王卓翔</span>
# 是bs4.element.Tag 类型,使用i.text获取标签间的内容
# 遍历得到当前跟进记录中的所有姓名
for i in names_tag:
namesRecord.append(i.text)
phones.append(phone)
names.append(name)
for i in times_tag:
timesRecord.append(i.text)
# 获取跟进记录内容
for i in content_tag:
# 因为从网页爬取的跟进记录中存在\n\r空格等字符,需要处理一下 只截取“xxx”中的内容
strT = i.text
# 获取字符串中字符首次出现的位置
startIdx = strT.find('“')
# 获取字符串中字符最后一次出现的位置
endIdx = strT.rfind('”')
strN = strT[startIdx + 1:endIdx]+""
contentRecord.append(strN)
return names,phones,namesRecord,timesRecord,contentRecord
# 将数据存到excel中
'''
:param names: 姓名列表
:param phones: 电话列表
:param namesRecord: 跟进人姓名列表
:param timesRecord: 跟进时间列表
:param contentRecord: 跟进内容列表
:param filename: 导出文件名称
:return: null
'''
def outPutExcel(names,phones,namesRecord,timesRecord,contentRecord,filename):
work_book = xlwt.Workbook(encoding='UTF-8')
worksheet = work_book.add_sheet('跟进记录')
size = len(names)
for i in range(1,size+1):
worksheet.write(i,1,names[i-1])
worksheet.write(i,2,phones[i-1])
worksheet.write(i,3,namesRecord[i-1])
worksheet.write(i,4,timesRecord[i-1])
worksheet.write(i,5,contentRecord[i-1])
work_book.save(filename)
def main():
header = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36 Edg/120.0.0.0",
"Cookie": '自己的cookie',
}
# url = 'https://aycrm.lvzhoucrm.com/admin.php/admin/user/details.html?id=991323'
nameList = []
phonesList = []
namesRecordList = []
timesRecordList = []
contentRecordList = []
idList = getIdListData()
print(len(idList))
baseHeaderUrl = 'https://aycrm.lvzhoucrm.com/admin.php/admin/user/details.html?id='
for idx,id in enumerate(idList):
print('开始爬取第'+str(idx)+"条数据")
url = baseHeaderUrl+str(id)
names, phones, namesRecord, timesRecord, contentRecord = getData(header, url)
# 追加每次爬取的数据
nameList.extend(names)
phonesList.extend(phones)
namesRecordList.extend(namesRecord)
timesRecordList.extend(timesRecord)
contentRecordList.extend(contentRecord)
# 爬取一次 休眠10秒
time.sleep(3)
outPutExcel(names=nameList,phones=phonesList,namesRecord=namesRecordList,timesRecord=timesRecordList,contentRecord=contentRecordList,filename='跟进记录.xls')
if __name__ == '__main__':
main()