基础笔记
#!user/bin/env pyhon3
# -*- coding:utf-8 -*-
# Email 23198899766@QQ.com
# Time : 2020/11/22 14:09
#range()的三种创建方式
#随机数
第一种
r=range(10)
print(r)
print(list(r))
#第二种
rr=range(1,10)
print(list(rr))
# 第三种
rrr=range(1,10,2)
print(list(rrr))
#in
p=(8 in r)
print(p)
# not in
p=(8 not in r)
print(p)
#p=list[1,6,8,9,7,5,6,9,8,7,6]
sum=0
a=1
while a<=100:
if a % 2 == 0:
sum += a
a+= 1
print(sum)
for in
for item in 'Python':
print(item)
for i in range(10):
print(i)
for _ in range(5):
print("人生苦短,我用python")
sum=0
for i in range(101):
if i%2==0:
sum+=i
print(sum)
#水仙花树
for item in range(100,1000):
ge=item%10
shi=item//10%10
bai=item//100
# print(ge,shi,bai)
#判断
if ge**3+shi**3+bai**3==item:
print(item)
# 三行四列的矩形
for i in range(1,4):#行
for j in range(1,5):#列
print('*',end='\t')
print()
# 九九乘法表
for i in range(1,10):
for j in range(1,i+1):
print(i,'*',j,'=',i*j,end='')
print()
#流程控制语句break与continue在二重循环中的是使用
for i in range(5):
for j in range(1,11):
if j%2==0:
# break
continue
print(j,end='\t')
print()
# 列表
lst=['hello','Python',100]
print(lst[0],end='\t')
lst2=list(['hello','Python',100])
print(lst2[-2])
List
#!user/bin/env python3
# -*- coding:utf-8 -*-
# Email 23198899766@QQ.com
# Time : 2020/11/24 22:12
lst=['hello','world',100,'hello']
#
print(lst.index(100))
# #范围内获取相同元素
print(lst.index('hello',1,4))
lst=['hello','world',100,'hello',9,6,8,7]
for i in lst.index():
print(i)
# 切片
# lst[start,stop,step]
lst=[10,20,100,30,9,6,8,7]
print(lst[1:5:1])#从1开始,到5结束,步长为1(默认)
print(lst[1:5:2])#步长为2
print(lst[:5:2])
print(lst[1::2])
print(lst[::2])
#步长是负数的时候
print("原列表",lst)
print(lst[::-1])
print(lst[7::-1])
print(lst[6:0:-2])
lst=[10,20,'Python','hello']
# 判断是否存在
print(10 in lst)
print(20 not in lst)
#遍历数组
for item in lst:
print(item)
# 添加元素
# 从尾部
lst=[10,20,30,'python']
lst2=[50,"pppp"]
lst.append(100)
print(lst)
# #末尾一次性添加多个元素
lst.extend(lst2)
print(lst)
#在指定位置添加元素
lst.insert(1,90)
print(lst)
#在任意位置上添加N多个元素
lst3=[True,'hello','yho']
lst[1:]=lst3
print(lst)
# 删除指定元素
lst=[10,20,30,40,6,66,48,99,20,5]
lst.remove(30)
print(lst)
# 根据索引删除元素
lst.pop(1)
print(lst)
# 不指定索引,那么久直接删除最后一个元素
lst.pop()
print(lst)
# 切片操作
new_list=lst[1:5] #1-4的元素
print("原列表",lst)
print("切片后的:",new_list)
# 不产生新的列表对象,而是删除原列表的内容
lst[1:5]=[]
print(lst)
# 清除列表中的所有元素
lst.clear()
print(lst)
# 将列表对象删除
del lst
print(lst)
lst=[10,20,30,40]
# 根据索引修改一个值
lst[2]=100
print(lst)
# 切片修改内容
lst[1:3]=[300,400,500] #去掉两位,添加新值
print(lst)
# # 列表元素的排序操作
lst=[20,40,30,50,10,70]
# 升序
lst.sort()
print(lst)
# 通过指定参数(reverse=True),实现降序
lst.sort(reverse=True)
print(lst)
# 使用内置函数,实现升序排序
lst=[20,40,30,50,10,70]
# 升序排序
new_lsit=sorted(lst)
print(new_lsit)
# 使用内置函数,实现降序排序
desc_list=sorted(lst,reverse=True)
print(desc_list)
# 灵活使用
lst=[i*i for i in range(1,10)]
print(lst)
# 输出[1, 4, 9, 16, 25, 36, 49, 64, 81]
String
#!user/bin/env python3
# -*- coding:utf-8 -*-
# Email 23198899766@QQ.com
# Time : 2020/11/29 22:52
# 字符串的驻留机制
a='Python'
b="Python"
c='''Python'''
print(a,id(a))
print(b,id(b))
print(c,id(c))
# 字符串的查询操作
s='hello,hello'
# # 查找第一次出现的位置
print(s.index('lo'))
print(s.find('lo'))
#
# # 查询最后一次出现的位置
print(s.rindex('lo'))
print(s.rfind('lo'))
# 对比查询不存在的内容
print(s.index('p'))#会抛异常
# find是常用的查询字符串的方法,可以返回一个值提供判断
print(s.find('p'))#给个 -1
print(s.rfind('p'))#给个 -1
print(s.rindex('p'))#会抛异常
# 字符串大小写转换操作的方法
# upper() 把字符串中所有字符都转成大写字母
# lower() 把字符串中所有字符都转成小写字母
# swapcase() 把字符串中所有大写字母转成小写字母,把所有小写字母都转成大写字母
# capitalize() 把第一个字符转换为大写,把其余字符转换为小写
# title() 把每个单词的第一个字符转换为大写,把每个单词的剩余字符转换为小写
#
s='hello,Python'
# # 都转换成大写
a=s.upper()
print(a)
#
# # 都转换成小写
print(s.lower())
#
# # 大小写互转
print(s.swapcase())
#
# # 每个英文单词的首字符都变成大写
print(s.title())
# 字符串内容对其操作的方法
#
# center() 居中对齐,第1个参数指定宽度,第2个参数指定填充符,第2个参数是可选的,默认是空格,如果设置宽度小于实际宽度则则返回原字符串左对齐,
# 第1个参数指定宽度,第2个参数指定填充符,第2个参数是可选的,默认是空格如
#
#
# ljust() 果设置宽度小于实际宽度则则返回原字符串字符串对齐右对齐,第1个参数指定宽度,第2个参数指定填充符,第2个参数是可选的,默认是空格如
#
# rjust() 果设置宽度小于实际宽度则则返回原字符串右对齐,左边用0填充,该方法只接收一个参数,用于指定字符串的宽度,如果指定的宽度
#
#
#zfill() #小于等于字符串的长度,返回字符串本身
#
#
#
s='hello Python'
# # 居中对齐
print(s.center(20,'*'))
# # 左对齐
print(s.ljust(20,'*'))
print(s.ljust(10))
print(s.ljust(20))
#
# # 右对齐
print(s.rjust(20,'*'))
print(s.rjust(20))
print(s.rjust(10))
# 右对齐,使用0填充
print(s.zfill(20))
print(s.zfill(10))
print('-4564'.zfill(8))
String Application
#!user/bin/env python3
# -*- coding:utf-8 -*-
# Email 23198899766@QQ.com
# Time : 2020/12/3 20:14
# 字符串分割
s='hello world Python'
# 没有指定分割符 通过空格实现分割
lst=s.split()
print(lst)
# 指定分割符
s1='hello|world|Python'
print(s1.split(sep='|'))
# 指定分割一个
print(s1.split(sep='|',maxsplit=1))
# rsplit()从右侧开始分割
print(s.split())
# 指定分割符
print(s1.rsplit(sep="|"))
# 指定分割一个
print(s1.rsplit(sep="|",maxsplit=1))
# 判断字符串操作的方法
# isidentifier() 判断指定的字符串是不是合法的标识符
# isspace() 判断指定的字符串是否全部由空白字符组成(回车、换行,水平制表符)
# isalpha() 判断指定的字符串是否全部由字母组成
# isdecimal( 判断指定字符串是否全部由十进制的数字组成
# isnumeric() 判断指定的字符串是否全部由数字组成
# isalnum() 列断指定字符串是否全部由字母和数字组成
s='hello,Python'
print('1',s.isidentifier())
print('2','hello'.isidentifier())
print('3','张三_'.isidentifier())
print('4','张三_123'.isidentifier())
# isspace() 判断指定的字符串是否全部由空白字符组成(回车、换行,水平制表符)
print('5','\t'.isspace())
# isalpha() 判断指定的字符串是否全部由字母组成
print('6','abc'.isalpha())
print('7','张三'.isalpha())
print('8','张三1'.isalpha())
# isdecimal( 判断指定字符串是否全部由十进制的数字组成
print('9','123'.isdecimal())
print('10','123四'.isdecimal())
# isnumeric() 判断指定的字符串是否全部由数字组成
print('11','123'.isnumeric())
print('12','123四'.isnumeric())
print('13','张三123'.isnumeric())
# isalnum() 列断指定字符串是否全部由字母和数字组成
print('14','我的ll'.isalnum())
print('15','abc1'.isalnum())
print('16','abc!'.isalnum())
# 字符串替换 replace() 第1个参数指定被替换的子串,
# 第2个参数指定替换子串的字符串,该方法返回替换后得到的字符串,
# 替换前的字符串不发生变化,调用该方法时可以通过第3个参数指定最大替换次数
#
# 字符串的合并 join() 将列表或元组中的字符串合并成一个字符串
s='hello,Python'
# 替换字符
print(s.replace('Python','Java'))
s1='hello,Python,Python,Python'
# 只替换相同对的两个字符
print(s1.replace('Python','Java',2))
lst=['hello','Java','Python']
# 合并列表
print('|'.join(lst))
# 合并列表
print(' '.join(lst))
# 合并元祖
t=('hello','Java','Python')
print(' '.join(t))
print('*'.join('Python'))
String Compare
#!user/bin/env python3
# -*- coding:utf-8 -*-
# Email 23198899766@QQ.com
# Time : 2020/12/3 20:59
# 进度条显示
import time
from tqdm import tqdm
for i in tqdm(range(1000)):
time.sleep(.01)
Set
#!user/bin/env python3
# -*- coding:utf-8 -*-
# Email 23198899766@QQ.com
# Time : 2020/11/27 16:08
# 什么是集合
# ·集合
# · Python语言提供的内置数据结构
# ·与列表、字典一样都属于可变类型的序列
# ·集合是没有value的字典
# 创建
# 第一种使用{}
s={2,3,4,5,6,7,7}#集合中的元素不能重复
# print(s)
# 第二种使用set
s1=set(range(6))
print(s1,type(s1))
s2=set([1,2,3,4,5,6,6])
print(s2,type(s2))
s3=set((1,2,3,4,4,5,6,65))
print(s3,type(s3))
s4=set('Python')
#集合中的元素是无序的
print(s4,type(s4))
#空集合
s5=set()
print()
Set Application
#!user/bin/env python3
# -*- coding:utf-8 -*-
# Email 23198899766@QQ.com
# Time : 2020/11/27 16:37
# 集合的相关操作
# ·集合元素的判断操作
# in或not in
# ·集合元素的新增操作
# ·调用add()方法,一次添中一个元素
# ·调用update()方法至少添中一个元素
# ·集合元素的删除操作
# ·调用remove()方法,一次删除一个指定元素,如果指定的元素不存在抛出KeyError
# ·调用discard()方法,一次删除一个指定元素,如果指定的元素不存在不抛异常
# ·调用pop()方法,一次只删除一个任意元素
# ·调用clear()方法,清空集合
s={10,20,30,40,50,666,444}
# 判断
# print(10 in s)
# print(100 in s)
# 新增 一次添加一个元素
s.add(80)
print(s)
# 新增 一次添加至少一个元素
s.update({200,300,400})
print(s)
s.update([500,999])
s.update((95,74))
print(s)
# 删除 如果没有对应值会报错
s.remove(500)
print(s)
#删除 如果没有对应值不会报错
s.discard(100)
print(s)
#删除 任意删除一个元素 不能指定参数
s.pop()
s.pop()
print(s)
#清空 clear
s.clear()
print(s)
Set Relation
#!user/bin/env python3
# -*- coding:utf-8 -*-
# Email 23198899766@QQ.com
# Time : 2020/11/27 22:53
# 集合间的关系
# ·两个集合是否相等
# ·可以使用运算符==或!=进行判断
# ·一个集合是否是另一个集合的子集
# ·可以调用方法issubset进行判断
# ·B是A的子集
# —个集合是否是另一个集合的超集
# ·可以调用方法issuperset进行判断
# ·A是B的超集
# ·两个集合是否没有交集
# ·可以调用方法isdisjoint进行判断
s={10,20,30,40}
s2={30,40,20,10}
# # 两个集合是否相等 集合是无序的
print(s==s2)
print(s!=s2)
# 一个集合是否是另一个集合的子集
s1={10,20,30,40,50,60}
s2={10,20,30,40}
s3={10,20,90}
print(s2.issubset(s1))
print(s3.issubset(s1))
# 一个集合是否是另一个集合的超集
print(s1.issubset(s2))
print(s1.issubset(s3))
# 两个集合是否含有交集
print(s2.isdisjoint(s3))
# 两个集合是否含有交集
s4={100,200,300}
print(s2.isdisjoint(s4))
# 交集
s1={10,20,30,40,50,60}
s2={10,20,30,40,70}
print(s1.intersection(s2))
print(s1 & s2) #intersection和&同等操作
# 并集操作
print(s1.union(s2))
print(s1 | s2) #union和| 是同等操作的
# 差集操作
print(s1.difference(s2))#s1-s2的结果
print(s1-s2)
# 对称差集
print(s1.symmetric_difference(s2))
print(s1 ^ s2)
# 列表生成式公式
lst=[i*i for i in range(10)]
print(lst)
# 集合生成式
sets={i*i for i in range(10)}
print(sets)
Time
#!user/bin/env python3
# -*- coding:utf-8 -*-
# Email 23198899766@QQ.com
# Time : 2020/12/2 15:57
# 格式 含义
# %Y 完整的年份
# %m 月份(01 - 12)
# %d 一个月中的第几天(01 - 31
# %H 一天中的第几个小时(24小时制,00 - 23)
# %M 分钟数(00 - 59)
# %S 秒(01 - 61)
import time
# 时间戳
# 从1970年1月1日00:00:00标准时区诞生到现在时间一共过了多少秒。
timestamp=time.time()
print(timestamp,type(timestamp))
# 有时候可能我们可能需要模仿一些IO请求,假装让程序休眠一下,所以需要用到time的sleep函数。
# 睡眠1秒
time.sleep(1)
# 本地时区需要用到time的localtime方法。
t=time.localtime()
print(t,type(t))
# localtime还能接收一个时间戳参数。
# 将时间戳转换成 struct_time 对象
t = time.localtime(1606395685.1878598) # type:time.struct_time
print(t, type(t))
# 简单的时间格式
t=time.ctime()
print(t,type(t))
# 同理,time.ctime()也是可以接收一个时间戳的。
t = time.ctime(1606395685.1878598) # type:str
print(t, type(t))
# # 日期格式 -> 字符串(strftime)
t=time.localtime()
t_str=time.strftime("%Y-%m-%d",t)
print(t_str,type(t_str))
# # 字符串日期 -> 日期(strptime)
t_str = "2020-11-02"
t_time = time.strptime(t_str, "%Y-%m-%d") # type:time.struct_time
print(t_time, type(t_time))
# 注:datetime和time是两个不同的类型,不能混用。!!!!!
from datetime import datetime
t = datetime.today() # type:datetime
print(t, type(t))
print(t.year) # 年份
print(t.month) # 月份
# 返回本地时间datetime.now()
t = datetime.now() # type:datetime
print(t,type(t))
# 返回标准时间datetime.utcnow()
t = datetime.now()
print("东八区时间:", t)
t = datetime.utcnow() # type:datetime
print("UTC时间:", t)
# 时间戳转datetime
# 有时候,我们拿到的,就是时间戳,那就只能转了。
# 时间戳
timestamp = time.time()
# print(timestamp)
print(f"timestamp:{timestamp},type:{type(timestamp)}")
# 时间戳转datetime
t = datetime.fromtimestamp(timestamp)
print(f"t:{t},type:{type(t)}")
# print(t)
datetime -> 字符串日期(strftime)
t = datetime.now()
str_datetime = t.strftime("%Y-%m-%d %H:%M:%S")
print(f"字符串日期:{str_datetime},type:{type(str_datetime)}")
# 字符串日期 -> datetime(strptime)
str_datetime = "2020-11-29 22:05:20"
t = datetime.strptime(str_datetime, "%Y-%m-%d %H:%M:%S")
print(f"t:{t},type:{type(t)}")
# 时间加减
#
# 这才是本次的重头戏,好像只有datetime这个包,才有时间加减的。
#
# 时间加减的具体用途很多,必须多久过期什么的,多久之后提醒,都需要提前计算时间,还是很重要的。
from datetime import datetime
import datetime as idatetime
# 可以发现,这个时间确实是+成功了。
#
# 但是自带的时间加减,有个题,只能加天,不能加月,甚至年。
#
# 如果想要时间+月等,还要自己写逻辑。
t = datetime.now()
print(f"当前时间:{t}")
three_day = t + idatetime.timedelta(days=3)
print(f"三天后时间:{three_day}")
from datetime import datetime
from dateutil.relativedelta import relativedelta
# datetime时间自由加减
t = datetime.now()
print(f"当前时间:{t}")
three_time = t + relativedelta(months=3)
print(f"三个月后时间:{three_time}")
one_year = t+relativedelta(years=1)
print(f"一年后时间:{one_year}")
up_year = t+relativedelta(years=-1)
print(f"去年这个时间:{up_year}")
# 总结
#
# 本篇主要讲述的是关于Python时间的相关总结,相对来说,更推荐使用datetime。
#
# 需要注意的是,time和datetime是俩类型,是不能混用的。
#
# 其实最重要的是字符串时间->时间类型,这个在实际中用的非常多!
#
# 顺便还有python-dateutil的relativedelta相关方法,能够自由加减时间,算是挺方便的。
yuanzu
#!user/bin/env python3
# -*- coding:utf-8 -*-
# Email 23198899766@QQ.com
# Time : 2020/11/27 15:17
# ·元组
# Python内置的数据结构之一,是一个不可变序列
# ·不可变序列与可变序列
# ·不变可变序:字符串、元组
# ·不变可变序列:没有增、删,改的操作·可变序列:列表、字典
# ·可变序列:“可以对序列执行增、删、改操作,对象地址不发生更改
#不可变序列
s='hello'
print(id(s))
s=s+'world'
print(id(s))
print(s)
# 可变序列(他们的id一样的)
lst=[10,20,30]
print(id(lst))
lst.append(300)
print(id(lst))
#正题,元祖的创建方式
#第一种 使用()
t=('Python','world',98)
print(t)
print(type(t))
#第二种创建方式 使用内置函数tuple()
t1=tuple(('Python','world',98))
print(t1)
print(type(t1))
# 空列表
lst=[]
lst1=list()
# 空字典
d={}
d2=dict()
# 空元祖
t4=()
t5=tuple()
print('空列表',lst,lst1)
print('空字典',d,d2)
print('空元祖',t4,t5)
t=(10,[20,30],40)
print(t)
print(t[0],type(t[0]))
print(t[1],type(t[1]))
print(t[2],type(t[2]))
#向元祖里面的列表里面添加元素
t[1].append(100)
print(t)
# 元祖的遍历
t=('Python','world',98)
# 第一种,使用索引
print(t[0])
# 第二种for in
for item in t:
print(item,end='\t')
操作Excal表
# 创建一个新的excal表格,并写入内容
#
import xlsxwriter
#创建一个新的xlsx文件(如果原有同名文件会被覆盖)
workbook = xlsxwriter.Workbook("Expenses01.xlsx")
#创建一个新的表单,默认名称为 “sheet1”,输入字符参数可指定名称
worksheet = workbook.add_worksheet()
expenses = (
['Rent', 1000],
['Gas' , 100],
['Food', 300],
['Gym' , 50],
)
#worksheet 默认是从0行、0列开始计数
row = 0
col = 0
#worksheet.write 方法将数据写入 xlsx 表格中
#参数依次为:行号、列号、数据、[格式]
for item, cost in (expenses):
worksheet.write(row, col , item)
worksheet.write(row, col + 1, cost)
row += 1
#显式关闭workbook,若不显式指定,则作用域结束后自动关闭
workbook.close()
------------------------------------------------------------
#####读取内容后,追加到excal表里
from xlrd import open_workbook
from xlutils.copy import copy
import requests
import json
# api
url = 'https://www.zhihu.com/api/v4/members/wisphilo?include=allow_message%2Cis_followed%2Cis_following%2Cis_org%2Cis_blocking%2Cemployments%2Canswer_count%2Cfollower_count%2Carticles_count%2Cgender%2Cbadge%5B%3F(type%3Dbest_answerer)%5D.topics'
# header的目的是模拟请求,因为该api设置了反爬取
header = {
'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.169 Safari/537.36'
}
doc = requests.get(url, headers=header) # 发起请求
doc.encoding = 'utf-8' # 设置编码为utf-8
data = json.loads(doc.text) # 将json字符串转为json<br><br>#根据位置查找数据
print('用户名:', data.get('name'))
# print('个人描述:', data.get('headline'))
# print('职务:' + data.get('employments')[0].get('job').get('name'))
# print('回答:', data.get('answer_count'))
# print('文章:', data.get('articles_count'))
# print('关注者:', data.get('follower_count'))
name=data.get('name')
r_xls=open_workbook("Expenses01.xlsx")#读取excal文件
row=r_xls.sheets()[0].nrows#获取已有行数
excal = copy(r_xls)#将xlrd的对象转化为xlwt的对象
table= excal.get_sheet(0)#获取要操作的sheet
#对excal表追加一行内容
table.write(row,0,'名字')
table.write(row,1,name)
excal.save("Expenses01.xlsx")
--------------------------------------------------------
!/usr/bin/env python
-*- coding:utf-8 -*-
from xlrd import open_workbook
from xlutils.copy import copy
r_xls = open_workbook("test.xls") # 读取excel文件
row = r_xls.sheets()[0].nrows # 获取已有的行数
excel = copy(r_xls) # 将xlrd的对象转化为xlwt的对象
table = excel.get_sheet(0) # 获取要操作的sheet
#对excel表追加一行内容
table.write(row, 0, '内容1') #括号内分别为行数、列数、内容
table.write(row, 1, '内容2')
table.write(row, 2, '内容3')
excel.save("test.xls") # 保存并覆盖文件
os库对文件的操作
#!/usr/bin/python
# -*- coding: UTF-8 -*-
# print("hello python")
#
#创建并写入文本
# fp=open('text.txt','a+')
# print('hello python',file=fp)
# fp.close()
#读取文件内容
# 第一种
# fp=open("text.txt",'r+',encoding="utf-8")
# data=fp.read()
# print(data,type(data))
# fp.close()
# 第二种
# fp=open('text.txt','r+')
# str=fp.read(5)#输出五个字符
# print("里面的内容为:",str)
# fp.close()
# 文件定位
# 打开一个文件
# fp=open('text.txt','r+')
# str=fp.read(5)#输出五个字符
# print("里面的内容为:",str)
# # 查找当前位置
# position=fp.tell()
# print("当前的位置是:",position)
# # 把指针再次重新定位到文件开头
# position=fp.seek(0,0)
# str=fp.read(5)
# print("重新读取字符串:",str)
# # 关闭打开的文件
# fp.close()
-----------------------------------------------------------------------
#os库的基本使用
# 重命名和删除文件
# rename()方法:
# rename()方法需要两个参数,当前的文件名和新文件名。
# os.rename(current_file_name, new_file_name)
#!/usr/bin/python
# -*- coding: UTF-8 -*-
import os
#重命名文件text.txt到test.txt
os.rename("text.txt","test.txt")
# 删除一个已经存在的文件test2.txt
os.remove("test.txt")
# 在当前目录下创建一个新目录test。
os.mkdir("newdir")
# 用chdir()方法来改变当前的目录
os.chdir("newdir")
# 进入newdir目录
# 将当前目录改为"/home/newdir"
os.chdir("/home/newdir")
# getcwd()方法显示当前的工作目录。
print(os.getcwd())
# rmdir()方法删除目录,目录名称以参数传递。
os.remove("newdir")
Dictionary
#!user/bin/env python3
# -*- coding:utf-8 -*-
# Email 23198899766@QQ.com
# Time : 2020/11/25 22:47
# ·字典的特点
# ·字典中的所有元素都是一个key-value对, key不允许重复,value可以重复
# ·字典中的元素是无序的
# ·字典中的key必须是不可变对象
# ·字典也可以根据需要动态地伸缩
# ·字典会浪费较大的内存,是一种使用空间换时间的数据结构
# {'key':value}
# 使用{}创建和应用
# 第一种创建方法
scores={'张三':100,'李四':98,'王五':45}
print(scores)
# 第二种创建方法dict函数自动识别
student=dict(name='jack',age=20)
print(student)
# 空字典
d={}
print(d)
# 字典中的元素获取
scores={'张三':100,'李四':98,'王五':45}
# # 第一种,使用[]
print(scores['张三'])
# 第二种get()方法
print(scores.get('张三'))
print(scores.get('玛琪'))#None
print(scores.get('玛琪',99))# 当键不存在时,99是给的默认值
# key的判断
scores={'张三':100,'李四':98,'王五':45}
print('张三' in scores)
del scores['张三']#删除指定的key-value对
scores.clear()
print(scores)
# 添加内容
scores['陈刘']=98
print(scores)
# 修改元素
scores['陈刘']=100
print(scores)
scores={'张三':100,'李四':98,'王五':45}
# 获取所以的key
# 将所有的key组成的视图转成列表
print(list(keys))
# 获取所有的value
values=scores.values()
print(values)
print(type(values))
#将所有的values组成的视图转成列表
print(list(values))
# 获取所有键值对
items=scores.items()
print(items)
#dict_items([('张三', 100), ('李四', 98), ('王五', 45)])
print(type(items))
# #转换之后的列表元素是由元祖组成[('张三', 100), ('李四', 98), ('王五', 45)]
print(list(items))
#字典元素的遍历
for item in scores:
print(item,scores[item],scores.get(item))
Dictionary Application
#!user/bin/env python3
# -*- coding:utf-8 -*-
# Email 23198899766@QQ.com
# Time : 2020/11/27 14:39
# 获取value 字典名[key]
# 删除key-value对 字典名.get(key)
# 修改/新增 del字典名[key]
# in /not in 字典名[key]=value
#字典生成式
items=['English','Chinese','Math']
prices=[98,95,100]
#upper()是大写的意思
d= {item.upper():price for item,price in zip(items,prices)}
print(d)
#这种方法可以将zip拆成两部分
print(list(d),list(d.values()))
操作Mysql
import MySQLdb
#查询数量
def Count(cur):
count=cur.execute('select * from Student')
print ('there has %s rows record' % count)
#插入
def Insert(cur):
sql = "insert into Student(ID,Name,Age,Sex)values(%s,%s,%s,%s)"
param = (2,'xiaoming',24,'boy')
cur.execute(sql,param)
#查询
def Select(cur):
n = cur.execute("select * from Student")
print ("------")
for row in cur.fetchall():
for r in row:
print (r)
print ("------" )
#更新
def Update(cur):
sql = "update Student set Name = %s where ID = 2"
param = ("xiaoxue")
count = cur.execute(sql,param)
#删除
def Delete(cur):
sql = "delete from Student where Name = %s"
param =("xiaoxue")
n = cur.execute(sql,param)
try:
conn=MySQLdb.connect(host='localhost',user='root',passwd='123456',db='python',port=3306)
cur=conn.cursor()
#数量
Count(cur)
#查询
Select(cur)
#插入
Insert(cur)
print "插入之后"
#查询
Select(cur)
#更新
Update(cur)
print "更新之后"
#查询
Select(cur)
#删除
Delete(cur)
print "删除之后"
#查询
Select(cur)
cur.close()
conn.close()
except MySQLdb.Error,e:
print "Mysql Error %d: %s" % (e.args[0], e.args[1])
selenium
特殊操作
send_keys后自动回车
这个就是不用找到点击按钮在点击
导包:from selenium.webdriver.common.keys import Keys
find...找到输入框.send_keys("内容", Keys.ENTER)
窗口的切换
1.切换到后面的窗口
当前面的点击一个会重新打开的一个click()后,开始切换窗口到跳转后的视角:driver.switch_to.window(web.window_handles[-1])#这个就是切换到当前的最后一个窗口,下标可以改
2.关闭一个,重定位
关闭当前定位的窗口
driver.close()
重定位到另一个窗口
driver.switch_to.window(web.window_handles[0])
处理iframe
切换进iframe
1.首先定位到iframe,复制iframe的xpath,使用selenium.find...xpath找到
2.注意!!!一个页面里面可能会有多个iframe,所以定位的时候,适用下标提前选择是哪个
3.使用driver.switch_to.frame(第一步定位的变量)
回到默认视角
也就是跳出iframe
driver.swicth_to.default_content()
scrapy常用命令
实例:
qsbk 普通类的scrapy + 翻页
wxapp crawl 类的scrapy
renren_login post请求 模拟登陆
douban_login post请求 模拟登陆
hyk 图片深度下载 + 翻页
boss crawl 类
useragent_demo 随机请求头和 IP
scrapy 安装
1、pip install scrapy
2、在windows 下,还需要安装pypiwin32 pip install pypiwin32
3、在ubuntu下,还需安装第三方库: sudo apt-get install python-devpython-pip libxml2-dev libxslt1-dev zlib1g-dev libffi-dev libssl-dev
scrapy框架详细步骤
一、创建爬虫项目Spider
1、选择合适的文件夹,在pycharm中的Terminal中输入: scrapy startproject XXX
2、cd Spider
3、创建爬虫文件
scrapy genspider -t crawl xxx_spider URL
4、新建 启动爬虫 文件
在根目录下新建 start.py
from scrapy import cmdline
cmdline.execute("scrapy crawl 爬虫名称".split())
二、修改爬虫文件
1、修改配置文件 setting
#(1)修改协议,请求头,下载路径
ROBOTSTXT_OBEY = False
from fake_useragent import UserAgent
DEFAULT_REQUEST_HEADERS = {
'Accept': 'text/html,application/json,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8',
'User-Agent':str(UserAgent(verify_ssl=False).random),
#'Referer': 'xxxxxx' , #下载图片/文件需要
#'Cookie': 'xxxxxx' , #在settings里设置默认cookie(从浏览器复制一下,或者bp抓包),COOKIES_ENABLED=False 必须为False
}
DOWNLOAD_DELAY = 0.5 # 设置延时下载
AUTOTHROTTLE_ENABLED = True #默认False;为True表示 启用AUTOTHROTTLE扩展
AUTOTHROTTLE_START_DELAY = 5 #默认3秒;初始下载延迟时间
AUTOTHROTTLE_MAX_DELAY = 180 #默认60秒;在高延迟情况下最大的下载延迟
#AUTOTHROTTLE_TARGET_CONCURRENCY = 1.0 # 设置 Scrapy应该与远程网站并行发送的平均请求数, 目前是以1个并发请求数
MEDIA_ALLOW_REDIRECTS =True # 允许重新定向(转到其他网址)
DOWNLOAD_WARNSIZE = 0 # 取消最大字节数限制
CONCURRENT_REQUESTS=10 # 并发设置为100
COOKIES_ENABLED=False # 禁止cookie 在settings里设置默认cookie(从浏览器复制一下,或者bp抓包)
RETRY_ENABLED=False # 打开重试开关
RETRY_TIMES=3 # 重试次数
RETRY_HTTP_CODES = [429,404,403] #重试对象
HTTPERROR_ALLOWED_CODES = [429] #上面报的是403,就把403加入
DOWNLOAD_TIMEOUT=200 # 超时时间为20s
IMAGES_EXPIRES = 90 # 90天的图片失效期限
FILES_EXPIRES = 90 # 90天的文件失效期限
#LOG_LEVEL = 'ERROR' # 日志只保留错误信息
#配置文件下载路径
import os
#FILES_STORE=os.path.join(os.path.dirname(os.path.dirname(__file__)),'文件下载路径')
FILES_STORE=os.path.join('D:\\siren','文件下载路径')
#配置图片下载路径
import os
#IMAGES_STORE=os.path.join(os.path.dirname(os.path.dirname(__file__)),'图片下载路径')
IMAGES_STORE=os.path.join('D:\\siren','图片下载路径')
(2)激活管道(图片下载管道和文件下载管道)
ITEM_PIPELINES = {
'scrapy.pipelines.images.ImagesPipeline': 1 #默认图片下载渠道
'scrapy.pipelines.files.FilesPipeline': 1 #默认文件下载渠道
'BMW.pipelines.***ImagesPipeline': 1 #自定义的图片下载渠道 宝马5系 与pipelines中的自定义类相对应
'mp3.pipelines.***FilesPipeline': 300, #自定义的文件下载渠道 mp3
}
(3)激活随机请求头
DOWNLOADER_MIDDLEWARES = {
'***.middlewares.UserAgentDownloadMiddleware': 543,
}
3、修改items
所需的字段=scrapy.Field() 如 name=scrapy.Field()
#文件
category = scrapy.Field() #文件夹名称
file_urls=scrapy.Field()
files=scrapy.Field()
#图片
category = scrapy.Field() #文件夹名称
image_urls=scrapy.Field()
images=scrapy.Field()
4、修改pipelines
定义3个函数
#保存文本
from scrapy.exporters import JsonLinesItemExporter
class ***Pipeline:
def open_spider(self,spider):
self.fp=open("XXXXXXXXX.json",'wb') #保存的文件名称
self.exporter=JsonLinesItemExporter(self.fp,ensure_ascii=False,encoding='utf-8')
def process_item(self, item, spider):
self.exporter.export_item(item) #以export方式导出,不以json方式导出
return item
def close_spider(self,spider):
self.fp.close()
print('爬虫结束....')
保存图片
from scrapy.pipelines.images import ImagesPipeline
from scrapy.pipelines.files import FilesPipeline
from scrapy.linkextractors import LinkExtractor
from scrapy.spiders import CrawlSpider, Rule
from . import settings
import os
#(1)、重新构建类(自定义)
class ***ImagesPipeline(ImagesPipeline):
def get_media_requests(self, item, info):
#将item与request进行绑定,用于获取 items 中的分类(category)
#这个方法是在发送下载请求前调用(这个函数本身就是去发送下载请求的)
request_objs=super(***ImagesPipeline,self).get_media_requests(item,info)
for request_obj in request_objs:
request_obj.item=item
return request_objs
def file_path(self, request, response=None, info=None):
# 这个方法是在图片将要被存储的时候调用,来获取这个图片存储的路径
path=super(***ImagesPipeline,self).file_path(request,response,info)
category=request.item.get('category') .replace('/','').replace('\','').replace(':','').replace('*','').replace('?','').replace('"','').replace('<','').replace('>','').replace('|','').replace(' ','') # 获取分类并规范名称
images_store=settings.IMAGES_STORE # 获取图片保存路径
category_path=os.path.join(images_store,category) #拼接文件夹路径
if not os.path.exists(category_path):
os.mkdir(category_path)
image_name=path.replace("full/","") #获取图片名称
image_path=os.path.join(category_path,image_name)
print('开始爬取: {}'.format(image_path))
return image_path
保存文件(分目录存储)
from scrapy.pipelines.images import ImagesPipeline
from scrapy.pipelines.files import FilesPipeline
from scrapy.linkextractors import LinkExtractor
from scrapy.spiders import CrawlSpider, Rule
from . import settings
import os
#(1)、重新构建类(自定义)
class ***FilesPipeline(FilesPipeline):
def get_media_requests(self, item, info):
#将item与request进行绑定,用于获取 items 中的分类(category)
#这个方法是在发送下载请求前调用(这个函数本身就是去发送下载请求的)
request_objs=super(***FilesPipeline,self).get_media_requests(item,info)
for request_obj in request_objs:
request_obj.item=item
return request_objs
def file_path(self, request, response=None, info=None):
# 这个方法是在图片将要被存储的时候调用,来获取这个图片存储的路径
path=super(***FilesPipeline,self).file_path(request,response,info) #文件名称-哈希编码 full/512f6cb1745800a2176e793fb449c3b90e676efa.wav
category=request.item.get('category') .replace('/','').replace('\\','').replace(':','').replace('*','').replace('?','').replace('"','').replace('<','').replace('>','').replace('|','').replace(' ','') # 获取分类并规范名称
images_store=settings.FILES_STORE # 获取文件保存路径
category_path=os.path.join(images_store,category) #拼接文件夹路径
#category_path=os.path.join(images_store) #文件路径可进行修改,取消文件夹,直接存到默认目录中
if not os.path.exists(category_path):
os.mkdir(category_path)
image_name=path.replace("full/","") #获取图片名称
# 文件路径可进行修改,取消文件夹,直接存到默认目录中
# img_path=path.split(".")[:-1]
# image_name=path.replace(img_path[0],category)
image_path=os.path.join(category_path,image_name)
print('开始爬取: {}'.format(image_path))
return image_path
保存文件(默认目录存储)
from scrapy.pipelines.images import ImagesPipeline
from scrapy.pipelines.files import FilesPipeline
from scrapy.linkextractors import LinkExtractor
from scrapy.spiders import CrawlSpider, Rule
from . import settings
import os
#(1)、重新构建类(自定义)
class ***FilesPipeline(FilesPipeline):
def get_media_requests(self, item, info):
#将item与request进行绑定,用于获取 items 中的分类(category)
#这个方法是在发送下载请求前调用(这个函数本身就是去发送下载请求的)
request_objs=super(***FilesPipeline,self).get_media_requests(item,info)
for request_obj in request_objs:
request_obj.item=item
return request_objs
def file_path(self, request, response=None, info=None):
# 这个方法是在图片将要被存储的时候调用,来获取这个图片存储的路径
path=super(***FilesPipeline,self).file_path(request,response,info) #文件名称-哈希编码 full/512f6cb1745800a2176e793fb449c3b90e676efa.wav
category=request.item.get('category') .replace('/','').replace('\\','').replace(':','').replace('*','').replace('?','').replace('"','').replace('<','').replace('>','').replace('|','').replace(' ','') # 获取分类并规范名称
images_store=settings.FILES_STORE # 获取文件保存路径
#category_path=os.path.join(images_store,category) #拼接文件夹路径
category_path=os.path.join(images_store) #文件路径可进行修改,取消文件夹,直接存到默认目录中
if not os.path.exists(category_path):
os.mkdir(category_path)
#image_name=path.replace("full/","") #获取图片名称
# 文件路径可进行修改,取消文件夹,直接存到默认目录中
img_path=path.split(".")[:-1]
image_name=path.replace(img_path[0],category)
image_path=os.path.join(category_path,image_name)
print('开始爬取: {}'.format(image_path))
return image_path
6、修改爬虫
普通爬虫
from scrapy.linkextractors import LinkExtractor
from scrapy.spiders import CrawlSpider, Rule
import scrapy,requests
from ..items import ***Item
class KekemeinvSpider(scrapy.Spider):
name = 'kekemeinv'
allowed_domains = ['www.keke234.com']
# 从start_requests发送请求
def start_requests(self):
page_list = ['http://mobilecdngz.kugou.com/api/v3/rank/song?rankid=8888&ranktype=2&page={}&pagesize=30&volid=&plat=2&version=8955&area_code=1'.format(i) for i in range(1, 30)]
for page_url in page_list:
yield scrapy.Request(url=page_url,callback=self.parse_page)
def parse_page(self,response)
url_list=response.xpath('//*[@id="a_ajax_5217174"]/@href').getall()
for url in url_list:
page_url='https://fj.91dfjh.pw/pw/'+url
yield scrapy.Request(url=page_url, callback=self.parse_item)
def parse_item(self, response):
#实例化item ,实现参数共享
item=*****Item()
item['category']=response.xpath('/html/body/main/div/div[1]/div[2]/div[1]/p/text()').get()
mp3_url='https://www.kugou.com/yy/index.php?r=play/getdata&hash={}&album_id={}'.format(hash,album_id)
yield scrapy.Request(url=mp3_url, callback=self.parse_detail, meta={"item": item})
def parse_detail(self,response):
#承接item传递过来的参数
item = response.meta['item']
down_urls=response.xpath('//*[@id="cms_player"]/iframe/@src').getall()
item["file_urls"] = down_urls
yield item
规则爬虫
import scrapyfrom scrapy.linkextractors import LinkExtractorfrom scrapy.spiders import CrawlSpider, Rulefrom ..items import ***Itemclass Mymp3Spider(CrawlSpider): name = 'mymp3' allowed_domains = ['sc.chinaz.com'] start_urls = ['http://sc.chinaz.com/yinxiao/index_2.html'] rules = ( Rule(LinkExtractor(allow=r'http://sc.chinaz.com/yinxiao/\d+\.htm'), follow=False), #列表页无需回调 Rule(LinkExtractor(allow=r'http://sc.chinaz.com/yinxiao/\d+\.htm'), callback='parse_item', follow=False), #详情页需要回调 )def parse_item(self, response): item = {} category = response.xpath('//h2/a/text()').get() # category 文件夹名称,单个文本 urls = response.xpath('//div[2]/div[1]/div[1]/div[3]/a[1]/@href').getall() # urls 图片下载地址,列表 urls = list(map(lambda url: response.urljoin(url), urls)) # 循环遍历urls,并将结果以列表的形式传入 item = **Item(category=category, file_urls=urls) # 不用字典,用Item格式回传到items yield item
三、运行爬虫
1、start.py 直接运行
2、命令行:scrapy crawl 爬虫的名称 scrapy crawl 爬虫的名称 -o 保存的文件名
#####################################
POST请求
1、推荐使用 scrapy .FormRequest 方法,方便的制定表单数据
2、重写 start_requests 方法,在这个方法中,发送post请求
【gif图片下载】
(1) scrapy.pipelines.images.py 文件中的 ImagesPipeline 类,继承ImagesPipeline类后,对image_downloaded方法进行重写
def check_gif(self, image): if image.format is None: return Truedef persist_gif(self, key, data, info): root, ext = os.path.splitext(key) absolute_path = self.store._get_filesystem_path(key) self.store._mkdir(os.path.dirname(absolute_path), info) f = open(absolute_path, 'wb') # use 'b' to write binary data. f.write(data)def image_downloaded(self, response, request, info): try: checksum = None for path, image, buf in self.get_images(response, request, info): if checksum is None: buf.seek(0) checksum = md5sum(buf) width, height = image.size if self.check_gif(image): self.persist_gif(path, response.body, info) else: self.store.persist_file( path, buf, info, meta={'width': width, 'height': height}, headers={'Content-Type': 'image/jpeg'}) return checksum except: pass
(2)修改 pipelines.py
import scrapy,osfrom scrapy.utils.misc import md5sumfrom scrapy.pipelines.images import ImagesPipelinefrom . import settingsclass ***ImagesPipeline(ImagesPipeline): def get_media_requests(self, item, info): #将item与request进行绑定,用于获取 items 中的分类(category) #这个方法是在发送下载请求前调用(这个函数本身就是去发送下载请求的) request_objs=super(***ImagesPipeline,self).get_media_requests(item,info) for request_obj in request_objs: request_obj.item=item return request_objs def file_path(self, request, response=None, info=None): # 这个方法是在图片将要被存储的时候调用,来获取这个图片存储的路径 path=super(***ImagesPipeline,self).file_path(request,response,info) #print(path) category=request.item.get('category') # 获取分类 images_store=settings.IMAGES_STORE # 获取图片保存路径 category_path=os.path.join(images_store) # 默认目录存储 路径 category_path=os.path.join(images_store,category) # 分目录存储 路径 #print(category_path) if not os.path.exists(category_path): os.mkdir(category_path) image_name=path.replace("full/","") .replace("jpg","gif") #获取图片名称,并替换为gif格式 #image_name="{}.gif".format(category) image_path=os.path.join(category_path,image_name) #print(image_path) return image_path def check_gif(self, image): if image.format is None: return True def persist_gif(self, key, data, info): root, ext = os.path.splitext(key) absolute_path = self.store._get_filesystem_path(key) self.store._mkdir(os.path.dirname(absolute_path), info) f = open(absolute_path, 'wb') # use 'b' to write binary data. f.write(data) def image_downloaded(self, response, request, info): try: checksum = None for path, image, buf in self.get_images(response, request, info): if checksum is None: buf.seek(0) checksum = md5sum(buf) width, height = image.size if self.check_gif(image): self.persist_gif(path, response.body, info) else: self.store.persist_file( path, buf, info, meta={'width': width, 'height': height}, headers={'Content-Type': 'image/jpeg'}) return checksum except: pass
###############################################################
5、修改下载器中间件middlewares(设置随机请求头)
(1)、重新定义下载器中间件(middlewares)
user-agent 设置随机的请求头中间键
from fake_useragent import UserAgentclass UserAgentDownloadMiddleware(object):def process_request(self,request,spider): user_agent=str(UserAgent().random) request.headers['User-Agent']=user_agent #print(request.headers['User-Agent'])
设置中间键破解防盗链接
class ReferMiddleWare(object):
def process_request(self,request,spider): refer=request.url if refer: request.headers['refer']=refer #print(request.headers['refer'])
proxy 设置随机的代理IP
class IPMiddleWare(object): PROXIES = [ 'http://171.11.178.36:9999', 'http://36.249.49.30:9999', 'http://124.65.136.2:8060', 'http://163.204.247.73:9999', 'http://163.204.241.89:9999', 'http://58.254.220.116:53579', 'http://122.5.107.51:9999' ] def process_request(self,request,spider): proxy = random.choice(self.PROXIES) request.meta['proxy'] = proxy
(2)、设置管道程序(setting)
DOWNLOADER_MIDDLEWARES = { '***.middlewares.ReferMiddleWare': 1, '***.middlewares.UserAgentDownloadMiddleware': 543, '***.middlewares.IPMiddleWare': 200,}
scrapy_crawlscrapy
scrapy框架
什么是框架?
就是一个集成了很多功能并且具有很强通用性的一个项目模板。
如何学习框架?
专门学习框架封装的各种功能的详细用法。
什么是scrapy?
爬虫中封装好的一个明星框架。功能:高性能的持久化存储,异步的数据下载,高性能的数据解析,分布式
scrapy框架的基本使用
环境的安装:
mac or linux:pip install scrapy
windows:
pip install wheel
下载twisted,下载地址为http://www.lfd.uci.edu/~gohlke/pythonlibs/#twisted
安装twisted:pip install Twisted‑17.1.0‑cp36‑cp36m‑win_amd64.whl
pip install pywin32
pip install scrapy
测试:在终端里录入scrapy指令,没有报错即表示安装成功!
创建一个工程:scrapy startproject xxxPro
cd xxxPro
在spiders子目录中创建一个爬虫文件
scrapy genspider spiderName www.xxx.com
执行工程:
scrapy crawl spiderName
scrapy数据解析
scrapy持久化存储
基于终端指令:
要求:只可以将parse方法的返回值存储到本地的文本文件中
注意:持久化存储对应的文本文件的类型只可以为:'json', 'jsonlines', 'jl', 'csv', 'xml', 'marshal', 'pickle
指令:scrapy crawl xxx o filePath
好处:简介高效便捷
缺点:局限性比较强(数据只可以存储到指定后缀的文本文件中)
基于管道:
编码流程:
数据解析
在item类中定义相关的属性
将解析的数据封装存储到item类型的对象
将item类型的对象提交给管道进行持久化存储的操作
在管道类的process_item中要将其接受到的item对象中存储的数据进行持久化存储操作
在配置文件中开启管道
好处:
通用性强。
面试题:将爬取到的数据一份存储到本地一份存储到数据库,如何实现?
管道文件中一个管道类对应的是将数据存储到一种平台
爬虫文件提交的item只会给管道文件中第一个被执行的管道类接受
process_item中的return item表示将item传递给下一个即将被执行的管道类
基于Spider的全站数据爬取
就是将网站中某板块下的全部页码对应的页面数据进行爬取
需求:爬取校花网中的照片的名称
实现方式:
将所有页面的url添加到start_urls列表(不推荐)
自行手动进行请求发送(推荐)
手动请求发送:
yield scrapy.Request(url,callback):callback专门用做于数据解析
五大核心组件
引擎(Scrapy)
用来处理整个系统的数据流处理, 触发事务(框架核心)
调度器(Scheduler)
用来接受引擎发过来的请求, 压入队列中, 并在引擎再次请求的时候返回. 可以想像成一个URL(抓取网页的网址或者说是链接)的优先队列, 由它来决定下一个要抓取的网址是什么, 同时去除重复的网址
下载器(Downloader)
用于下载网页内容, 并将网页内容返回给蜘蛛(Scrapy下载器是建立在twisted这个高效的异步模型上的)
爬虫(Spiders)
爬虫是主要干活的, 用于从特定的网页中提取自己需要的信息, 即所谓的实体(Item)。用户也可以从中提取出链接,让Scrapy继续抓取下一个页面
项目管道(Pipeline)
负责处理爬虫从网页中抽取的实体,主要的功能是持久化实体、验证实体的有效性、清除不需要的信息。当页面被爬虫解析后,将被发送到项目管道,并经过几个特定的次序处理数据。
请求传参
使用场景:如果爬取解析的数据不在同一张页面中。(深度爬取)
需求:爬取boss的岗位名称,岗位描述
图片数据爬取之ImagesPipeline
基于scrapy爬取字符串类型的数据和爬取图片类型的数据区别?
字符串:只需要基于xpath进行解析且提交管道进行持久化存储
图片:xpath解析出图片src的属性值。单独的对图片地址发起请求获取图片二进制类型的数据
ImagesPipeline:
只需要将img的src的属性值进行解析,提交到管道,管道就会对图片的src进行请求发送获取图片的二进制类型的数据,且还会帮我们进行持久化存储。
需求:爬取站长素材中的高清图片
使用流程:
数据解析(图片的地址)
将存储图片地址的item提交到制定的管道类
在管道文件中自定制一个基于ImagesPipeLine的一个管道类
get_media_request
file_path
item_completed
在配置文件中:
指定图片存储的目录:IMAGES_STORE = './imgs_bobo'
指定开启的管道:自定制的管道类
中间件
下载中间件
位置:引擎和下载器之间
作用:批量拦截到整个工程中所有的请求和响应
拦截请求:
UA伪装:process_request
代理IP:process_exception:return request
拦截响应:
篡改响应数据,响应对象
需求:爬取网易新闻中的新闻数据(标题和内容)
1.通过网易新闻的首页解析出五大板块对应的详情页的url(没有动态加载)
2.每一个板块对应的新闻标题都是动态加载出来的(动态加载)
3.通过解析出每一条新闻详情页的url获取详情页的页面源码,解析出新闻内容
CrawlSpider:类,Spider的一个子类
全站数据爬取的方式
基于Spider:手动请求
基于CrawlSpider
CrawlSpider的使用:
创建一个工程
cd XXX
创建爬虫文件(CrawlSpider):
scrapy genspider t crawl xxx www.xxxx.com
链接提取器:
作用:根据指定的规则(allow)进行指定链接的提取
规则解析器:
作用:将链接提取器提取到的链接进行指定规则(callback)的解析
#需求:爬取sun网站中的编号,新闻标题,新闻内容,标号
分析:爬取的数据没有在同一张页面中。
1.可以使用链接提取器提取所有的页码链接
2.让链接提取器提取所有的新闻详情页的链接
分布式爬虫
概念:我们需要搭建一个分布式的机群,让其对一组资源进行分布联合爬取。
作用:提升爬取数据的效率
如何实现分布式?
安装一个scrapy redis的组件
原生的scarapy是不可以实现分布式爬虫,必须要让scrapy结合着scrapy redis组件一起实现分布式爬虫。
为什么原生的scrapy不可以实现分布式?
调度器不可以被分布式机群共享
管道不可以被分布式机群共享
scrapy redis组件作用:
可以给原生的scrapy框架提供可以被共享的管道和调度器
实现流程
创建一个工程
创建一个基于CrawlSpider的爬虫文件
修改当前的爬虫文件:
导包:from scrapy_redis.spiders import RedisCrawlSpider
将start_urls和allowed_domains进行注释
添加一个新属性:redis_key = 'sun' 可以被共享的调度器队列的名称
编写数据解析相关的操作
将当前爬虫类的父类修改成RedisCrawlSpider
修改配置文件settings
指定使用可以被共享的管道:
ITEM_PIPELINES = {
'scrapy_redis.pipelines.RedisPipeline': 400
}
指定调度器:
# 增加了一个去重容器类的配置, 作用使用Redis的set集合来存储请求的指纹数据, 从而实现请求去重的持久化
DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter"
# 使用scrapy redis组件自己的调度器
SCHEDULER = "scrapy_redis.scheduler.Scheduler"
# 配置调度器是否要持久化, 也就是当爬虫结束了, 要不要清空Redis中请求队列和去重指纹的set。如果是True, 就表示要持久化存储, 就不清空数据, 否则清空数据
SCHEDULER_PERSIST = True
指定redis服务器:
redis相关操作配置:
配置redis的配置文件:
linux或者mac:redis.conf
windows:redis.windows.conf
代开配置文件修改:
将bind 127.0.0.1进行删除
关闭保护模式:protected mode yes改为no
结合着配置文件开启redis服务
redis server 配置文件
启动客户端:
redis cli
执行工程:
scrapy runspider xxx.py
向调度器的队列中放入一个起始的url:
调度器的队列在redis的客户端中
lpush xxx www.xxx.com
爬取到的数据存储在了redis的proName:items这个数据结构中