学习python爬虫的一些基础
1.浏览器
一个网页加载的过程
1.服务器端渲染
html的页面的内容和数据在服务器进行配合。
在浏览器端看到的页面源代码中,有需要的内容
2.客户端渲染(浏览器)
html的内容和数据进行融合是发生在浏览器。(通过浏览器和服务端端交互进行渲染,如:通过js代码发送ajax请求),通过浏览器的devtools的network可以看到请求交互过程
在解析过程中,devtools中element不一定准确,内容有可能是经过js处理后的,要以页面源代码或response.text为准来解析数据
2.Requests
爬虫的本质就是用程序模拟浏览器上的请求。
爬虫程序不是人,很有可能生成病毒,爬取过程中不应该去触碰个人隐私信息。在浏览器看不到的数据,爬虫是无法爬取的。
requests模块式python的一个非常完善的第三方模块,专门用来模拟浏览器进行网络请求的发送。
requests常用的请求方式,get、post
get、post是http的请求方式,只需要根据抓包里面的状况去选择具体用那种方式去请求。
get和post的请求方式不同
get的请求参数最终被放在url中,params和url传参二选一
query string parmas
resp = requests.get('www.baidu.com', params={
"ie":'utf8',
'wd':'周杰伦'
})
resp = requests.get('www.baidu.com?ie=utf8&wd=周杰伦)
post的请求参数放在请求body中。
post 常规请求参数 FormData, Request Payload
requests.post('url', data={字典})
requests.post('url',json={字典})
requests.post(url, data=json.dumps{字典}, headers={
"content-Type":"application/json;charset=utf-8"
})
状态码(http状态码)
200 系列 指当前本次和服务器通信ok
300 系列 指重定向
写爬虫的时候基本上不用管302,因为requests可以自动重定向到新的页面
400 系列
404 url不存在,在服务器上无法响应该内容
403 被服务器拦截,访问权限问题
500 系列 表示服务器内部出现错误
如果浏览器正常,但是爬虫程序的时候出现500错误,基本上就是参数错误,让服务器无法正常 工作。
timeout, 服务器连接失败 多种因素引起的,最好的解决办法就是:
for i in range(5):
try:
发送请求
break
except Exception as e:
print(e)
3.数据解析
html
re --> .*? 想拿到html中获取js的一部分代码(字符串)
xpath --> 用来解析常规的html结构
from lxml import etree
tree = etree.HTML(resp.text)
ret = tree.xpah('//div/fd/')
# etree的xpath默认返回的是列表。
if ret:
ret[0]
else:
xxx
bs4 --> 解析xml、 svg的时候
find()
findall()
json
resp.json()
json.loads(resp.text)
如果遇到反爬,就可能拿到的内容和抓包工具不一致,一般先打印resp.text,确定返回的内容是json格式,才开始转化
jsonp
4.反爬(header)
请求头:
User-Agent 用户发送请求的设备
Cookie 服务器记录在浏览器上的一个字符串,保存在本地,记录用户信息,再次发送请求的时候自动携带,作用是和服务器之间保持会话。session 服务器端对应的内容。因为http请求是一个无状态请求,无法像socket一样保持会话。
1. 可以直接从浏览器抓包中复制(某些网站可以)
2. 用requests.session()来保持会话, requests.session()可以处理set-cookie的内容,但不能处理javascript处理的cookies的内容。如果网站用js维护cookies,就需要自己通过带来来处理逻辑。
Referer 检测上一页页面的url是什么,一般直接复制粘贴就可以
网页自己添加的参数 一般都需要手工逆向来处理
请求头一般通过测试来选择要添加的内容。一般加user-anagent。
响应头:
Location 302 重定向地址
Set-Cookie session自动维护
网页自己添加的参数 一般情况网站是没有的,除非是特殊网站
session的使用
session = requests.session()
session.headers={}
session.cookie = {}
session.get()
session.post()
5.多任务异步爬虫 (多线程、多进程、协程)
进程是资源单位,进程之间是隔离开的
全局变量 a
p1 = Process()
p2 = Process()
在p1中修改a的值,在p2中没有任何变更,因为两个进程是隔离开的,p1,p2相当于两个程序
Queue队列或redis 两个进程之间的数据交换
协程
# 爬虫案例
import asyncio
import aiohttp
from lxml import etree
url = "17k.com/"
async def download_one(url):
async with aiohttp.ClientSession() as session:
# requsets.sesson()
async with session.get(url) as resp:
page_source = await resp.text()
tree = etree.HTML(page_source) # 不是io操作需要计算,不需要挂起
print(tree.xpah("//table//text()"))
async def main():
tasks = []
for i in range(1.6):
url = "https://www.17d.com/all/book/2_0_0_0_0_0_0_{i}.html"
# 每一个url对应一个任务
task = asyncio.create_task(download_one(url))
tasks.append(task)
await asyncio.wait(tasks)
if__name__ == '__main__':
asyncio.run() # 相当于
event_loop = asynico.get_event_loop()
event_loop.run_until_complete(main())
面临的每一个网站的设计思路和反爬思路不一样,在实际爬取过程中,根据不同的网站,来灵活运用,见招拆招。
6.数据存储(Mongodb,Mysql, Redis,Excel)
csv 本质就是一个文本文件
写操作
数据之间用,分隔
f = open("data.csv", mode='w', encoding='utf-8')
f.write("1")
f.write(",")
f.write("张伟")
f.write(",")
f.write("5000")
f.write("\n")"
用pandas读取
import pandas
r = pandas.read_csv("data.csv", sep=",", header=None) # sep 分隔符号
print(r)
输出为excel
r.to_excel('data.xlsx', header=False, index=False)
Mysql 关系型数据库, 以表格的形式进行存储,关系型数据库依赖于表格,表格与表格之间可以建立关系,通过主外键关系建立关联,主键唯一的表示一条数据,将主键作为另一张表的外键。
关系有: 一对一关系;一对多关系;多对多关系
create table 表名(
字段 类型 约束
...)
-- 一般可以通过navicat工具来创建表
# 增加数据
insert into 表(字段1, 字段2, 字段3...) values(值1, 值2, 值3...)
还有一种方式insert into 表 values(值1, 值2, 值3...) 一般不用这种方式,容易出现混乱
# 修改数据
update 表 set 字段=值, 字段= 值 where id = 1
# 删除数据
delete from 表 where 条件
# 查询数据
select * from 表 where 条件;
python 操作数据库
import pymysql
from pymysql.cursors import DictCursor
# 1.创建连接
conn = pymysql.connect(
user='root',
password=,
host='127.0.0.1',
database='student',
port=3306,
charset="utf8mb4",
)
# 2.创建游标
# cursor = conn.cursor() # 普通游标, 数据获取后是一个元组
try:
cursor = conn.cursor(DictCursor) # 字典游标,数据获取后是一个字典
sql = 'select * from user'
cursor.execute(sql)
cursor.fetchone() # 获取一条数据
cursor.fetchall() # 获取所有数据
cursor.fetchmany(size=None) # 获取部分数据
# 3. 新增数据
sql = 'insert into 表名(键1,键2,键3)values (值1, 值2, 值3)'
# 提交事务
cursor.execute(sql)
# 如果不报错
conn.commit() # 数据增加后提交
except Exception as e:
print(e)
# 如果报错
conn.rollback() # 回滚
finally:
conn.close()
reids 是以key:value的形式存储数据
key -> 数据(单一字符串, 列表, 字典,set)
数据类型和key
常用的数据类型:
1.string
set key value
get key 查看一条数据
incr key 让该key对应的数据自增(原子性,安全)
incrby key count 让该key对应的value自增 count
type key 查看数据类型
set name 张三 # 添加数据name=张三
get name # 查看数据 张三
set age 10
get age # 10
incr age # 11
get age #11
incrby age 5 # 16
-
hash
哈希,相当于字典,常见操作
hset key k1 v1 将k1, v1存在key上
hget key k1 将key上的k1提取
hmset key k1 v1 k2 v2 k3 v3 一次性将多个k,v存储在key
hmget key k1 k2 一次性将key中的k1 k2 提取出来
hgetall key 一次性将key中的内容全部提取出来
hkeys key 将key中所有的k全部提取
kvals key 将key中的所有v提取
HMSET stu id 1 name 张三 age 18 HNGET stu name age # 张三 18 HMGETALL stu # id 1 name 张三 age 18 HEYS stu # id name age KVALS stu # 1 张三 18
-
list 列表,底层是要给双向列表,可以从左或从右插入
常见操作
lpush key 数据1 数据2 数据3 从左边插入数据
rpush key 数据1 数据2 数据3 从右边插入数据
lrange key start stop # 从start到stop提取数据
llen key 返回key列表长度
lpop key 从左边删除一个数据并返回 被删除的元素
rpop key 从右边删除一个数据并返回被删除的元素
lpush classname 1班 2班 3班 lrange classname 0 -1 # 1班 2班 3班 rpush classname 4班 lrange ban -1 # 1班 2班 3班 4班 lpop classname # 1班 rpop classname # 4班 llen classname # 3
-
set
set是无需的超大集合,无需不重复
常见操作
sadd key 值 向集合内存入数据
smembers key 查看集合内所有元素
scard key 常看集合中元素的个数
sismember key val 查看集合中时候包含val这个元素
sunion key1 key2 key1和key2 两个集合的并集
sdiff key1 key2 key1和key2两个集合的差集, 在key1集合中,但不在key2集合中
sinter key1 key 2 计算交集, 在key1和key2中都出现了
spop key 随机中key中删除一个数据
srandmember key count 随机从key中查询count个数据
sadd start 姜维 李明 王红 张强 # 4 add start 李明 # 0 重复的数据是无法存储进去的 semebers start # 姜维 李明 王红 张强 sismember start 李明 # 李明在不在集合start中在就返回1 不在返回0
5.zset 有序集合,有序集合的元素也具有唯一性,并且存储的数据也是redis最基础的string数据,但是在存储数据的同时还增加了一个score表示分值, redis就是通过这个score作为排序的规则
常用操作
zadd key s1 m1 s2 m2 # 在向key中存入m1 m2 分数分别是 s1 s2
zrange key start stop 【withsores】 # 查看从start到stop中的所有数据【是否要分数】
zrevrange key start stop 倒叙查看start到stop的数据
zcard key 查看zset数据元素的个数
zcount key min max 查看分数在min和max之间的数据量
zincrby key score member 将key中member的分值score
zscore key m 查看key中m的分值
zadd familay 1 爸爸 2 儿子 3 女儿
zrange family 0 -1 withscores # 正序查看
zrevarange family 0 -1 withscores # 倒叙查看
zincrby family 5 女儿 # 给女儿加5分
zadd family 10 儿子 # 将儿子的分数改为100分
zscore family 爸爸 # 查看爸爸的分值
zcard familay # 查看整个家庭的数据个数
所有的set集合基本都是无序的,zset的底层是给每一个数据增加一个分值,通过分值进行排列
python操作redis
import redis
conn = redis.Redis(
host='localhost',
port=6379,
db=4
)
conn.set('myname', "joy")
r = conn.get('myname')
print(r) # b'joy'
7.面向对象(基础)
核心:编程实现的转变。
面向过程的爬虫: (函数的执行)
1. 获取页面代码
2. 解析页面代码
3. 存储数据
面向对象的爬虫:通过操作对象,实现爬虫。 (类的实现,通过属性的赋值和方法的实现爬虫)
面向对象的三大特性:封装、继承、多态