Python函数、爬虫API接口寻找
day16
01-豆瓣电影top250写入csv
承接上周最后的内容,csv的读写,对豆瓣电影top250的数据进行写入,改动不算太大,直接上码
import requests
import time
from random import randint
from bs4 import BeautifulSoup
from tqdm import tqdm
import csv
f = open('豆瓣电影top250.csv', 'wt', encoding='utf-8', newline='')
file_write = csv.writer(f)
columns = ['电影标题', '电影评分', '电影评论人数', '中心思想']
file_write.writerow(columns)
for i in tqdm(range(0, 10)):
# print(f'---------这是第{i + 1}页----------')
URL = f'https://movie.douban.com/top250?start={i * 25}&filter='
Headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36 Edg/112.0.1722.34'
}
response = requests.get(url=URL, headers=Headers)
# print(response.text)
# result = '正常' if response.status_code == 200 else response.status_code
# print(result)
soup = BeautifulSoup(response.text, 'html.parser')
lilist = soup.select('html > body > div#wrapper ol > li')
# print(lilist)
for i in lilist:
# 电影标题
movieTitle = i.select('li > div.item > div.info > div.hd > a > span.title')
movieList = [i.text for i in movieTitle]
movieTitle = ''.join(movieList)
# 电影评分
movieScore = i.select_one('li div.bd > div.star > span.rating_num').text
# # 电影评论人数
commPeople = i.select_one('li div.bd > div.star > span:nth-child(4)').text
# 中心思想
middIdea = i.select_one('li div.bd > p.quote > span.inq')
middIdea = middIdea.text if middIdea != None else ''
# print(movieTitle, movieScore, commPeople, middIdea)
data = [movieTitle, movieScore, commPeople, middIdea]
file_write.writerow(data)
# 每一页爬取完毕,休眠一下,防止我们被检测到
# sleep:休眠,强制等待几秒钟
time.sleep(randint(1, 3))
f.close()
print('写入完成')
# 通过观察发现,pycharm打开的csv文件和txt文本文件几乎没有差别
# csv文件虽然是以表格的样式展示数据,但是csv文件的本质时文本文件
02-with语法
大抵就是不用随时补文件关闭,有个印象就行
# 生成一个有学生成绩的CSV文件
import csv
file = open('学生成绩.csv', 'w', encoding='utf-8', newline='')
myWrite = csv.writer(file)
col = ['姓名', '成绩']
myWrite.writerow(col)
# 文件读写完一定要关闭文件
file.close()
# with语法:with语法经常和open方法搭配使用
# with:预定义的清理操作:当一个对象长期没有被使用时,就会被清理。
with open('学生成绩2.csv', 'w', encoding='utf-8', newline='') as file_1:
# open() as a和a = open()一样
pass
# 只要with缩进中的代码执行完了,那么文件所占用的进程或线程就会被清理掉(可以理解为文件被关闭)
03-数据类型转换
eval方法:能将字符串类型的任意数据、表达式转换为其原来的样子,让其具有原来的功能。
str1 = '{"name":"张三"}'
print(eval(str1), type(eval(str1)))
# 返回:{'name': '张三'} <class 'dict'>
a = eval('1 + 2 + 3 + 4')
print(a)
# 返回:10
eval方法直接把字符串转换为代码,有可能将指令写为字符串用eval执行了,存在安全问题
import ast
a1 = ast.literal_eval()
# literal_eval相对于eval方法,不会还原字符串类型的表达式,也就意味着如果字符串中有恶意指令,不会去执行
04-函数
一、函数的概念
此函数非彼函数f(x),python中的函数是组织好的、可重复使用的、用来实现单一功能或者相关联功能的代码块
二、函数的作用
函数能够提高代码的重复利用率(降低代码的冗余),我们所说的方法:input、print、type等本质就是函数
# 案例:计算1-N的和
total = 0
for i in range(1, 101):
total += i
print(total)
total_1 = 0
for i in range(1, 1001):
total_1 += i
print(total_1)
# 以上两组代码就是重复代码
def numTotal(N):
total = 0
for i in range(1, N + 1):
total += i
return total
print(numTotal(1000))
print(numTotal(10000))
三、自定义函数
想要自定义函数,需要遵循以下规则
1.创建一个函数,要以def关键字开头,后面接函数名称、圆括号()、形参(非必写项)、冒号:
2.函数内部的代码的第一行语句可以选择性的使用多行注释对函数的功能进行说明(非必写项)
3.函数内部使用return将函数内产生的结果返回到函数外部
4.如果不适用return,函数的结果为None
5.注意代码缩进
def a():
total = 0
for i in range(1, 3):
total += i
print(total)
print(a())
# 返回:3、None
函数内部和函数外部时两个互相独立的个体,如果想要使两者联通,必须使用return将函数内部的结果返回到函数外部,返回出来的结果就是这个函数产生的结果,只要函数内部出发了return,那这个函数立即停止运行
四、函数的调用
已经定义好的函数,并不会自动执行,需要单独调用这个已经存在的函数,调用语法:函数名(实参) --> 实参和形参要相对应,形参不写,实参也不能写
day17
01-函数的参数
函数在被定义时可以设置形参,参数代表我们调用函数时,向函数内部传入的值,那么形参应该如何定义以及实参应该如何传递?
一、默认值参数
函数定义时,给设置的形参提前赋值,在定义函数时,将形参写为:参数名=数据 的形式,此时,这个形参便有了默认值,如果调用函数时没有给此形参赋值,那么这个形参便使用默认值,如果调用函数时给这个形参赋了值,那么这个形参便使用新的值
def numTotal(N = 100):
total = 0
for i in range(1, N + 1):
total += i
return total
print(numTotal())
# 返回:5050
print(numTotal(1000))
# 返回:500500
二、位置参数
在定义函数时,函数中参数以什么样的顺序去定义的,那么调用函数时就必须以什么样的顺序去传入实参
即实参和形参位置要一一对应
def numTotal_1(start, end):
"""
计算start到end的和
"""
total = 0
for i in range(start, end + 1):
total += i
return total
print(numTotal_1(1, 100))
# 返回:5050
三、关键字参数
如果使用位置参数进行传参,实参和形参的位置要一一对应,那么如果不想按照顺序传参,就可以使用关键字参数
print(numTotal_1(start=1, end=100))
print(numTotal_1(end=100, start=1))
能不能位置参数、关键字参数同时用?
print(numTotal_1(1, end=100))
# print(numTotal_1(end=100, 1))
1.位置参数必须写在关键字参数之前 2.位置参数必须按照顺序写 3.关键字参数顺序随便
四、不定长参数
def arguement(a, b, c, *args, **kwargs):
print(a, b, c)
print(args)
print(kwargs)
arguement(1, 100, 200, 300, d=100, e=200, f=300)
# 返回:1 100 200
# (300,)
# {'d': 100, 'e': 200, 'f': 300}
# *args --> positional argument
# **kwargs --> keyword arguement
*args在前,**kwargs在后
*args和**kwargs都能接受N个参数(N >= 0)
02-函数的参数类型说明
函数在定义时,形参可以添加数据类型说明。
1.在定义函数时,可以使用”冒号:数据类型“的形式对形参进行说明
2.在定义函数时,还可以通过给形参定义初始值(默认值)的形式对此形参接受的值的类型进行说明
3.还可以告诉其他人此函数的返回结果,使用 --> 数据类型 的形式表示
def func_1(num: int, string='') -> str:
"""
这个函数是什么功能的函数
"""
return ''
func_1(10)
03-练习题
练习题:给定一个日期,判断这个日期是当年的第几天。例如:2023/1/1 --> 这是今年的第一天
date = input('请输入一个日期(例如:2023/1/1):')
date_list = date.split('/')
year = int(date_list[0])
# 判断年份是否是闰年
if year % 400 == 0:
flag = True
else:
flag = False
# 先将每个月的天数罗列下来
month_day = [0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
# 天数的计算
month = int(date_list[1])
if month > 2 and flag:
days = sum(month_day[0:month]) + int(date_list[2]) + 1
else:
days = sum(month_day[0:month]) + int(date_list[2])
print(f'{date}是当年的第{days}天')
用函数封装的做法
def is_leap_year(year:int) -> bool:
"""
判断一个年份是否是闰年
"""
if year % 400 == 0:
return True
else:
return False
def main(data:str) -> int:
"""
程序执行的主函数
"""
# 将年月日分开
datalist = data.split('/')
# 判断一个年份是否是闰年(函数之间可以相互调用)
result = is_leap_year(int(datalist[0]))
# 罗列每个月的天数
month_day = [0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
# 天数的计算
month = int(datalist[1])
if month > 2 and result:
days = sum(month_day[0:month]) + int(datalist[2]) + 1
else:
days = sum(month_day[0:month]) + int(datalist[2])
return f'{data}是当年的{days}天'
# 调用main函数,得到最终的天数
print(main('2000/3/2'))
day18
01-登录注册大作业
1.使用登录或注册功能时,程序能够打印菜单。
2.实现账号注册功能(注册新帐号时,保证账号是独一无二的)。
3.实现账号登录功能(登陆时要有账号是否存在的判断)。
4.登录和注册可以分开写,分成两个python文件。
5.确保新账号注册成功时已存在的旧帐号不能消失。
先奉上我自己写的,很乱,没有打印菜单,因为不知道菜单出来了怎么做选择,勉强写写了,主要是字符串转字典,再把字典中的键值对拿出来比对
先是注册模块
import ast
str1 = input('请输入注册用户名:')
str2 = input('请输入注册账号:')
str3 = input('请输入注册密码:')
judge = open('Userinfo.txt', 'rt', encoding='utf-8')
data1 = str(judge.readlines())
data2 = ast.literal_eval(data1)
list1 = []
list2 = []
for i in data2:
data3 = ast.literal_eval(i)
list1.append(data3)
# print(list1)
dict1 = {}
dict1.setdefault(str1, str2)
dict1.setdefault(str2, str3)
keys_1 = dict1.keys()
for i in list1:
keys_2 = i.keys()
list2.append(keys_2)
write = open('Userinfo.txt', 'at', encoding='utf-8')
if keys_1 not in list2:
write.write(str(dict1)+'\n')
else:
print('该用户已存在')
judge.close()
write.close()
再是登录模块
import ast
str1 = input('请输入账号:')
str2 = input('请输入密码:')
dict1 = {}
dict1.setdefault(str1, str2)
user = open('Userinfo.txt', 'rt', encoding='utf-8')
data1 = str(user.readlines())
data2 = ast.literal_eval(data1)
list1 = []
list2 = []
for i in data2:
data3 = ast.literal_eval(i)
list1.append(data3)
# print(list1)
for i in list1:
for j in i.items():
list2.append(j)
data4 = tuple(dict1.items())[0]
# print(data4)
# print(list2)
if data4 in list2:
print('登录成功')
else:
print('用户不存在或者密码错误')
接下来是用函数封装好了的答案
import ast
def readFile(path):
with open(path, 'r', encoding='utf-8') as file:
info = file.read()
return info
# print(readFile('./菜单.txt'))
def writeFile(path, userinfo):
with open(path, 'w', encoding='utf-8') as file:
info = file.write(userinfo)
return info
def register():
username = input('请输入要注册的账号:')
password = input('请输入要注册的密码:')
userinfo = ast.literal_eval(readFile('./Userinfo.txt'))
# 将用户名(键)和密码(值)组成键值对
# 在字典中做成员运算,默认是判断某个键是否存在
if username not in userinfo:
userinfo[username] = password
writeFile('./Userinfo.txt', str(userinfo))
print('注册成功')
else:
print('用户名已存在,请重新输入')
def login():
username = input('请输入账号:')
password = input('请输密码:')
userinfo = ast.literal_eval(readFile('./Userinfo.txt'))
if username in userinfo and userinfo[username] == password:
print('登陆成功')
else:
print('用户名不存在或者账号密码错误')
def main():
while True:
menu = readFile('菜单.txt')
print(menu)
choice = input('请输入你的选择:')
if choice == '1':
login()
elif choice == '2':
register()
elif choice == '3':
print('系统已退出')
# 如果实在没有需要返回的内容,可以单独写return
return
else:
print('输入错误请重新输入')
main()
02-函数作业
难度都不大
1.编写一个函数,提取指定字符串中的所有的字母,然后拼接在一起产生一个新的字符串并返回,例如:传入’12a&bc12d-+’ --> ‘abcd’
def letter(str) -> str:
str_1 = ''
for i in str:
if 'a' <= i <= 'z' or 'A' <= i <= 'Z':
str_1 += i
return print(str_1)
letter('12a&bc12d-+')
2.写入一个自己的capitalize函数,能够将指定字符串的首字母变成大写字母并返回,例如:‘abc’ -> ‘Abc’ ‘12asd’ --> ‘12asd’
# 这个理解错了题意,理解成了字符串中的第一个字母变大写
def capitalize(str) -> str:
str3 = ''
if 'a' <= str[0] <= 'z':
str1 = str[0]
str2 = str1.upper()
str3 = str2 + str[1:]
else:
for i in str:
if 'a' <= i <= 'z':
str1 = i.upper()
num = str.index(i)
str3 = str[0:num] + str1 + str[num + 1:]
break
return print(str3)
capitalize('12abcdef')
# 正确答案
def selfCapitalize(string:str) -> str:
if 'a' <= string[0] <= 'z':
return chr(ord(string[0]) - 32) + string[1:]
else:
return print(string)
capitalize('12abcdef')
3.写一个自己的endswith函数,判断一个字符串是否以指定的字符串结束,例如:字符串1:‘abc231ab’ 字符串2:‘ab’ 函数结果为:True;字符串1:‘abc231ab’ 字符串2:‘ab1’ 函数结果为:False
# 自己写的
def endswith(str_1, str_2) -> bool:
flag = True
if str_1[-2:] == str_2:
flag = True
else:
flag = False
return print(flag)
endswith(str_1='abc231ab', str_2='ab')
# 老师写的
def selfEndswith(string_1, string_2) -> bool:
lenght = len(string_2)
if string_1[-lenght:] == string_2:
return True
else:
return False
result = selfEndswith(string_1='abc231ab', string_2='ab')
print(result)
4.写一个自己的isdigit函数,判断一个字符串是否是纯数字字符串,例如:‘1234921’ 结果:True;‘23函数’ 结果:False;‘a2390’ 结果:False
def isdigit(str) -> bool:
flag = True
for i in str:
if 0 <= int(i) <= 9:
flag = True
else:
flag = False
break
return print(flag)
isdigit('123123')
def selfIsdight(string:str) -> bool:
for i in string:
if '0' <= i <= '9':
continue
else:
return False
return True
result = selfIsdight('123456')
print(result)
# 前提:在函数内部,如果break和return同时在一条分支出现,如果合适,可以直接使用return。
03-全局变量和局部变量
一、概念
根据变量的作用域不同我们将变量划分为全局变量和局部变量两种。
二、全局变量
定义在函数和类的外面的变量都叫做全局变量。
全局变量的作用域:从变量定义开始到程序结束的任意位置
三、局部变量
定义在函数内的变量叫做局部变量,局部变量的作用域:从定义开始到函数结束
四、我们因为面向对象的思想,所以优先选择局部变量。
局部变量被创建时,一定调用到了其所在函数或者类,系统会自动为这个函数或类创建一块独立的内存空间专门用来保存这个函数内部定义的变量,当函数的调用结束时,这个内存空间会被销毁。
五、使用
x = 100
def func1():
# 此x非彼x
x = 200
print(x) #200
func1()
print(x) #100
def func2(x):
print(x) #100
func2(x)
六、global和nonlocal --> 函数体的关键字,只能在函数内部使用
1.global:在函数中定义或修改全局变量,相当于将函数内外的内存打通了
y = 1
def func3():
global y
y = 2
print(y) #2
func3()
print(y) #2
def func4():
global z
z = 5
print(z) #5
func4()
print(z) #5
2.nonlocal:在函数的函数中定义或修改函数中的变量,相当于将函数的函数内外的内存打通了
def func5():
i = 8
print(i) #8
def func1():
nonlocal i
i = 9
print(i) #9
func1()
print(i) #8
func5()
04-使用函数封装的形式写爬虫
写的是一个链家二手房的爬虫
import requests
import csv
from bs4 import BeautifulSoup
from tqdm import tqdm
def get_response(link:str) -> str:
"""负责发送请求得到响应结果,获得网页源代码"""
Headers = {
'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36 Edg/112.0.1722.39'
}
response = requests.get(url=link, headers=Headers)
return response.text if response.status_code == 200 else response.status_code
def get_data(html_tree):
"""负责提取页面信息"""
lilist = html_tree.select('html > body > div.content ul.sellListContent > li')
data = []
for i in lilist:
# 二手房标题
houseTitle = i.select_one('li > div.info.clear > div.title > a').text
# 二手房总价
priceInfo = i.select('li > div.info.clear > div.priceInfo span')
total_price, unit_price = priceInfo[0].text + '万元', priceInfo[1].text
# print(houseTitle, unit_price, total_price)
data.append([houseTitle, unit_price, total_price])
return data
def main():
"""程序执行的主入口"""
file = open('./链家二手房.csv', 'w', encoding='utf-8', newline='')
csv.writer(file).writerow(['标题', '单价', '总价'])
for page in tqdm(range(1, 101), desc = '链家二手房爬虫'):
URL = f'https://cd.lianjia.com/ershoufang/pg{page}/'
# 1.先请求连接,拿到网页源代码
htmlStr = get_response(URL)
# 2.解析网页源代码转换为树结构
soup = BeautifulSoup(htmlStr, 'html.parser')
# 3.提取信息,同时写入这一页的多条数据
result = get_data(soup)
csv.writer(file).writerows(result)
file.close()
# 从此处开始调用函数
main()
05-API数据接口提取
一、API数据接口概念
API接口是负责传输数据的,在如今的网站中,除了极个别很古老的网站,大部分网站都会采用API接口进行数据的传输,为什么API接口传输数据这么受欢迎?因为API接口能够节省开发时间,节省开发成本。
二、API接口长什么样
https://v.api.aa1.cn/api/api-qq-gj/index.php?qq=xxxxx&num=xx&vip=x
API接口本身就是一个网页链接,但是API接口是有构造方式的。一个API接口由请求地址和请求参数构成,请求地址和请求参数使用?连接一个API接口可能涉及很多个请求参数,每个请求参数要形成key=value的形式,多个请求参数使用&连接
三、接口信息
https://v.api.aa1.cn/api/api-qq-gj/index.php?qq=1489267238&num=60&vip=1
接口中数据是这个样子
{“code”:“1”,“qq”:“1489267238”,“money”: “62”,“dengji”:"60级 ",“vip”:“非会员”}
在认知中这属于python中的字典,但是接口中的数据不是字典,可以转为字典
四、接口中的数据为json数据
json数据类型独立于所有编程语言,但是可以被所有编程语言使用
06-json数据类型
{
"JSON认知": "JSON是一种存储和交换数据的语法,具有自我描述性和易于理解的性质,json独立于所有编程语言,但是支持大部分的编程语言",
"JSON为什么能交换数据": "当数据在浏览器和服务器之间进行交换时,这些数据只能是文本。json属于文本,并且我们能够把所有的数据转换为JSON,然后发送给服务器,也能从服务器接收到任何形式的json数据,进而实现数据的解析和转义。",
"为什么JSON是最好的选择": "因为json数据足够轻量,保存在json中的中文等符号会被自动转化为编码表中的十六进制数据",
"json语法": "1.数据在键值对中。2.每个键值对使用逗号隔开。3.一个json数据是一个对象,使用{}保存。4.一个[]可以保存多个对象",
"字符串类型": "json中的字符串必须使用双引号包围",
"数字类型": 18,
"考试成绩": 99.5,
"是否成年": true,
"空值": null,
"json中的数组": [
"张三",
"李四",
"王五"
]
}
07-json数据如何转字典
python中有一个专门处理json数据的模块:json模块(库);json模块中有一个loads方法,可以将字符串类型的json数据转为字典
# json数据拿到以后就变成了字符串
jsonString = '{"name":"张三","age":18,"grow_up":true,"girl_friend":null}'
import json
print(type(jsonString))
data = json.loads(jsonString)
print(data, type(data))
# 返回:{'name': '张三', 'age': 18, 'grow_up': True, 'girl_friend': None} <class 'dict'>
实例:
import requests
import json
URL = 'https://v.api.aa1.cn/api/api-qq-gj/index.php?qq=1489267238&num=60&vip=1'
# 一般请求接口可以不加headers,但是如果确实拿不到信息,就再添加headers参数
response = requests.get(url=URL)
result = response.text
# print(result)
data = json.loads(result)
print(data)
# 返回:{'code': '1', 'qq': '1489267238', 'money': '62', 'dengji': '60级 ', 'vip': '非会员'}
08-如何寻找API接口
优先看返回数据的大小,但是真的很不好找,找到了就很好
API接口里就有你想要的信息,并不是所有的网站的API接口都能找到,如果找不到API接口,只能再使用其他方式(requests+css选择器等)。