Python实战:简易网络爬虫教程(以研招网招生信息为例)
写在前面
一是必要工作,二是代码解析,三是直接上手使用(无需基础),如果只是当作工具可只看一三部分
一.准备工作
1.安装python和pycharm(这里不多解释,放上别人的链接)
Python安装教程(新手)_python tcl/tk support-CSDN博客
2.更新pip:
python -m pip install --upgrade pip
3.安装对应模块:打开anaconda prompt(或者在pycharm终端(Terminal)中)依次输入:
pip install requests
pip install beautifulsoup4
pip install pandas
pip install lxml
# beautifulsoup4后面加个4,对应当前版本,不加可能会pip失败
4.创建python文件,导入对应模块
import requests
from bs4 import BeautifulSoup
from pandas.core.frame import DataFrame
import re
requests:网络请求模块
re:正则表达式
pandas:基于Numpy的处理文本或者表格数据的工具
beautifulsoup:用于从HTML和XML文件中提取数据
二.开搞
-
函数部分:定义一个名为
Graduate
的类,用于封装爬取数据的相关方法: -
__init__(self, province, category, provinceName)
:初始化方法,设置请求头和空的数据列表,并接受省份代码、专业代码和省份名称作为参数。def __init__(self, province, category, provinceName): self.head = { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKi" "t/537.36 (KHTML, like Gecko) Chrome/64.0.3282.186 Safari/537.36" } ##创建字典head,key是 "User-Agent",value是一个标识浏览器的用户代理字符串,用于模拟浏览器发送请求 self.data = [] #创建空列表用于存储爬取的数据 self.province = province #存储省份代码 self.category = category #存储专业代码 self.provinceName = provinceName #存储省份名称
-
get_list_fun(self, url, name)
:获取提交表单代码的方法,发送 GET 请求并将响应的 JSON 数据写入文件。def get_list_fun(self, url, name): """获取提交表单代码""" response = requests.get(url, headers=self.head) #get方法发送爬取请求 resprovince = response.json() #从响应对象中获取 JSON 格式的数据,并将其转换为 Python 对象 with open(name + ".txt", "w") as f: #打开文件 name + ".txt",以写入模式打开("w"),并使用 with 上下文管理器,确保文件在退出上下文时被正确关闭。文件对象存储在变量 f 中。 for x in resprovince: #遍历解析出的 JSON 数据 f.write(str(x)) f.write("\n")
-
get_list(self)
:获取省份、学科门类和专业编号数据,并写入对应的文本文件。def get_list(self): """ 分别获取省,学科门类,专业编号数据 写入txt文件 """ self.get_list_fun("http://yz.chsi.com.cn/zsml/pages/getSs.jsp", "province") #传递省份数据的 URL 和文件名 "province" self.get_list_fun('http://yz.chsi.com.cn/zsml/pages/getMl.jsp', "category") #传递学科门类数据的 URL 和文件名 "category" self.get_list_fun('http://yz.chsi.com.cn/zsml/pages/getZy.jsp', 'major') #传递专业编号数据的 URL 和文件名 "major"
-
get_school_url(self)
:发送 POST 请求获取学校网址列表,并通过正则表达式提取出学校的网址。def get_school_url(self): """ 输入省份, 发送post请求,获取数据 提取数据 必填省份,学科门类,专业可选填 返回学校网址 """ url = "https://yz.chsi.com.cn/zsml/queryAction.do" data = { "ssdm": self.province, "yjxkdm": self.category, } response = requests.post(url, data=data, headers=self.head) html = response.text reg = re.compile(r'(<tr>.*? </tr>)', re.S) #使用 re.S 标志,表示点 (.) 可以匹配换行符 content = re.findall(reg, html) schools_url = re.findall('<a href="(.*?)" target="_blank">.*?</a>', str(content)) #从匹配的字符串中提取所有学校网址,即 <a> 标签中 href 属性的值,并将这些网址存储在列表 schools_url 中 return schools_url
-
get_college_data(self, url)
:获取一个学校所有学院的数据,发送 GET 请求获取页面内容,并通过正则表达式提取出学院的网址,方法与4类似。def get_college_data(self, url): """返回一个学校所有学院数据""" response = requests.get(url, headers=self.head) html = response.text colleges_url = re.findall( '<td class="ch-table-center"><a href="(.*?)" target="_blank">查看</a>', html) return colleges_url
-
get_final_data(self, url)
:获取一个学校一个学院一个专业的数据,发送 GET 请求获取页面内容,并使用 BeautifulSoup 解析页面,提取出包含招生信息的表格数据。def get_final_data(self, url): """输出一个学校一个学院一个专业的数据""" temp = [] response = requests.get(url, headers=self.head) html = response.text soup = BeautifulSoup(html, features='lxml') #使用 BeautifulSoup 解析 HTML 内容,生成 BeautifulSoup 对象 soup,此处使用的是 'lxml' 解析器 summary = soup.find_all('td', {"class": "zsml-summary"}) for x in summary: temp.append(x.get_text()) self.data.append(temp) ##将包含当前学校、学院、专业等相关信息的临时列表 temp 利用append方法添加到类 self.data 中
-
get_schools_data(self)
:获取所有学校的数据,遍历学校网址列表,依次获取每个学校的招生信息。def get_schools_data(self): """获取所有学校的数据""" url = "http://yz.chsi.com.cn" schools_url = self.get_school_url() amount = len(schools_url) #计算学校网址列表的长度,即学校的数量,并将其存储在变量 amount 中 i = 0 for school_url in schools_url: i += 1 url_ = url + school_url #构造当前学校的完整 URL # 找到一个学校对应所有满足学院网址 colleges_url = self.get_college_data(url_) print("已完成" + self.provinceName + "第" + str(i) + "/" + str(amount) + "个高校爬取") #time.sleep(1) for college_url in colleges_url: _url = url + college_url self.get_final_data(_url)
-
get_data_frame(self)
:将列表形式的数据转换为 DataFrame,并保存为 CSV 文件。def get_data_frame(self): """将列表形数据转化为数据框格式""" data = DataFrame(self.data) data.to_csv(self.provinceName + "查询招生信息.csv", encoding="utf_8_sig") #.to_csv() 方法用于将数据写入 CSV 文件,其中的参数 encoding="utf_8_sig" 指定了 CSV 文件的编码为 UTF-8,以确保能够正确地处理中文字符
-
main部分:定义要抓取的省份代码列表
provinceList
和省份代码与名称的对应字典provinceNmaeDict
。然后,遍历省份代码列表,为每个省份创建一个Graduate
实例,调用get_schools_data
方法获取数据,并调用get_data_frame
方法将数据保存为 CSV 文件。if __name__ == '__main__': provinceList = [ '11' ] # 要抓取的省份代码 provinceNmaeDict = { '11': '北京市' } #省份代码和对应的省份名称 category = "0839" #指定要抓取的专业代码 for i in provinceList: province = i if province in provinceNmaeDict.keys(): spyder = Graduate(province, category, provinceNmaeDict[province]) spyder.get_schools_data() spyder.get_data_frame() #调用get_data_frame 方法,将获取到的数据转换为数据框格式,并保存为 CSV 文件
运行该程序,
同级目录下生成了一个csv文件,我们可以找到该文件的本地地址,用wps或者excel打开
效果如图,爬取成功
三.写在最后
直接使用,需要修改以下两点:
1.将main部分的provincelist和provinceNmaeDict的省份代码只保留自己想查询的,如北京(11)
2. category的专业代码换成自己想要查询的,打开研招网硕士专业目录可以看到对应编号,如电子科学与技术(080900)
完整代码如下:
import requests
from bs4 import BeautifulSoup
from pandas.core.frame import DataFrame
import re
class Graduate:
def __init__(self, province, category, provinceName):
self.head = {
"User-Agent":
"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKi"
"t/537.36 (KHTML, like Gecko) Chrome/64.0.3282.186 Safari/537.36"
}
self.data = []
self.province = province
self.category = category
self.provinceName = provinceName
def get_list_fun(self, url, name):
"""获取提交表单代码"""
response = requests.get(url, headers=self.head)
resprovince = response.json()
with open(name + ".txt", "w") as f:
for x in resprovince:
f.write(str(x))
f.write("\n")
def get_list(self):
"""
分别获取省,学科门类,专业编号数据
写入txt文件
"""
self.get_list_fun("http://yz.chsi.com.cn/zsml/pages/getSs.jsp",
"province")
self.get_list_fun('http://yz.chsi.com.cn/zsml/pages/getMl.jsp',
"category")
self.get_list_fun('http://yz.chsi.com.cn/zsml/pages/getZy.jsp',
'major')
def get_school_url(self):
"""
输入省份,
发送post请求,获取数据
提取数据
必填省份,学科门类,专业可选填
返回学校网址
"""
url = "https://yz.chsi.com.cn/zsml/queryAction.do"
data = {
"ssdm": self.province,
"yjxkdm": self.category,
}
response = requests.post(url, data=data, headers=self.head)
html = response.text
reg = re.compile(r'(<tr>.*? </tr>)', re.S)
content = re.findall(reg, html)
schools_url = re.findall('<a href="(.*?)" target="_blank">.*?</a>',
str(content))
return schools_url
def get_college_data(self, url):
"""返回一个学校所有学院数据"""
response = requests.get(url, headers=self.head)
html = response.text
colleges_url = re.findall(
'<td class="ch-table-center"><a href="(.*?)" target="_blank">查看</a>',
html)
return colleges_url
def get_final_data(self, url):
"""输出一个学校一个学院一个专业的数据"""
temp = []
response = requests.get(url, headers=self.head)
html = response.text
soup = BeautifulSoup(html, features='lxml')
summary = soup.find_all('td', {"class": "zsml-summary"})
for x in summary:
temp.append(x.get_text())
self.data.append(temp)
def get_schools_data(self):
"""获取所有学校的数据"""
url = "http://yz.chsi.com.cn"
schools_url = self.get_school_url()
amount = len(schools_url)
i = 0
for school_url in schools_url:
i += 1
url_ = url + school_url
# 找到一个学校对应所有满足学院网址
colleges_url = self.get_college_data(url_)
print("已完成" + self.provinceName + "第" + str(i) + "/" +
str(amount) + "个高校爬取")
#time.sleep(1)
for college_url in colleges_url:
_url = url + college_url
self.get_final_data(_url)
def get_data_frame(self):
"""将列表形数据转化为数据框格式"""
data = DataFrame(self.data)
data.to_csv(self.provinceName + "查询招生信息.csv", encoding="utf_8_sig")
if __name__ == '__main__':
provinceList = [
'11', '12', '13', '14', '15', '21', '22', '23', '31', '32', '33', '35',
'36', '41', '42', '43', '44', '51', '52', '53', '61', '62'
] # 要抓取的省份代码
provinceNmaeDict = {
'11': '北京市',
'12': '天津市',
'13': '河北省',
'14': '山西省',
'15': '内蒙古自治区',
'21': '辽宁省',
'22': '吉林省',
'23': '黑龙江省',
'31': '上海市',
'32': '江苏省',
'33': '浙江省',
'34': '安徽省',
'35': '福建省',
'36': '江西省',
'37': '山东省',
'41': '河南省',
'42': '湖北省',
'43': '湖南省',
'44': '广东省',
'45': '广西壮族自治区',
'46': '海南省',
'50': '重庆市',
'51': '四川省',
'52': '贵州省',
'53': '云南省',
'54': '西藏自治区',
'61': '陕西省',
'62': '甘肃省',
'63': '青海省',
'64': '宁夏回族自治区',
'65': '新疆维吾尔自治区',
'71': '台湾省',
'81': '香港特别行政区',
'82': '澳门特别行政区'
} #省份代码和对应的省份名称
category = "083903" #专业代码
for i in provinceList:
province = i
if province in provinceNmaeDict.keys():
spyder = Graduate(province, category, provinceNmaeDict[province])
spyder.get_schools_data()
spyder.get_data_frame()
作者:pinduangduang