# pythonSpider
**Repository Path**: zengshijun/pythonSpider
## Basic Information
- **Project Name**: pythonSpider
- **Description**: 学习路飞学城IT的代码练习
- **Primary Language**: Python
- **License**: Not specified
- **Default Branch**: master
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 0
- **Forks**: 2
- **Created**: 2023-03-11
- **Last Updated**: 2023-03-11
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
# 教程简介
+ 这个是在B站的一个在线课程,
+ up主:**路飞学城IT**
+ 一共*100*P
+ 学习内容包括:`http`、`request`、`正则`、`bs4`、`xpath`、`异步`、`协程`、`aiohttp`、`selenium`、`scrapy`、`CrawlSpider`、`asyncio`...
+ 课程名称:2020年Python爬虫全套课程(学完可做项目)
+ 课程地址:[https://www.bilibili.com/video/BV1Yh411o7Sz](https://www.bilibili.com/video/BV1Yh411o7Sz)
+ 学习课件:[https://book.apeland.cn/details/68/](https://book.apeland.cn/details/68/)
+ 路飞学诚 爬虫的课件 链接:[https://pan.baidu.com/s/1KWBu4oswrPZMD9K_4lFe8A](https://pan.baidu.com/s/1KWBu4oswrPZMD9K_4lFe8A) 密码:`91pl`
+ 目前学习到`23P`
# 1、爬虫简介
## 1.1、爬虫分类
### 1.1.1、通用爬虫
+ 就是获取一个url,把整个页面==所有内容==爬取下来
### 1.1.2、聚焦爬虫
+ 聚焦爬虫是建立在通用爬虫上面的
+ 先把整个页面爬取下来,取出自己想要的==部分内容==
### 1.1.3、增量式爬虫
+ 增量爬虫是检测网站的更新情况
## 1.2、反爬
### 1.2.1、robots.txt协议
+ 规定了网站那个部分的内容允许爬取。
+ ==非强制==,可遵守可不遵守(*君子协议*)
> 可以直接打开robots.txt查看
>
> [这是B站的一个robots.txt https://www.bilibili.com/robots.txt](https://www.bilibili.com/robots.txt)
下面是B站的robots协议

# 2、http和https协议
## 2.1 http协议
### 2.1.1、 请求头信息
#### 2.1.1.1、User-Agent
+ 请求载体的身份标识(包括系统版本,浏览器版本)
#### 2.1.1.2、Connection
+ 请求完毕之后,是断开连接还是保持连接
### 2.1.2、响应头信息
#### 2.1.2.1、Content-Type
+ 服务器响应之后返回的数据类型
## 2.2 https协议
### 2.2.1、与http的区别
+ 唯一区别是安全与不安全,
+ ***https***是安全的***http***是不安全的
### 2.2.2、加密的方式
#### 2.2.2.1、对称秘钥加密
+ 在客户端进行加锁,
+ 把秘钥和加密信息一起传送个服务器,
+ 然后服务器用传送过来的秘钥解锁加密信息

> 弊端:如果使用软件拦截,把秘钥和加密信息拦截到,也是可以进行解锁的。
#### 2.2.2.2、非对称秘钥加密
1. 服务器把公钥给客户端
2. 客户端用公钥对文件加密,然后 把密文发给服务器
3. 服务器用私钥进行解密

> * 公钥和私钥[https://zhuanlan.zhihu.com/p/31477508](https://zhuanlan.zhihu.com/p/31477508)
> * 理论上只要私钥不泄露就有加密效果
>
> ***缺点***
>
> 1. 效率比较低,处理更加复杂
> 2. 还是会有安全隐患
> 1. 如果在服务器发送公钥的时候被拦截
> 2. 拦截之后串改公钥,然后发给客户端
> 3. 拦截密文,用自己串改之后的私钥解密,就可以得到内容
#### 2.2.2.3、证书秘钥加密
`https采用的加密方式`

1. 服务器向认证机构发送公钥,
2. 认证机构对公钥进行==数字签名==,并把秘钥和数字签名绑定在一起形成==数字证书==
3. 服务器发送数字证书给客户端,客户端通过验证==数字签名==判断证书的真伪
4. 信息无误,客户端就可以使用公钥进行加密,把密文发送给服务器
5. 服务器使用私钥解密,读取文件

# 3、requests模块
> * requests模块包括两个模块
> * ***urllib*** (比较古老,不常用)
> * ***requests*** (现在用这个比较多)
> * 用来模拟浏览器发送请求。
## 3.1、环境安装
在终端进行环境安装
```shell
pip install requests -i https://pypi.tuna.tsinghua.edu.cn/simple
或者如果装有Anaconda可以使用命令conda
conda install requests
```
## 3.2、实战编码
### 3.2.1、爬取搜狗首页的数据
```python
## 导入requests
import requests
def spider1():
## 使用requests的get请求方法,请求搜狗引擎的首页
response = requests.get(url="https://www.sogou.com/")
## 打印输出首页的内容
print(response.text)
## 保存为sogou.html
with open("./sogou.html","w+",encoding="utf-8") as f:
## 写入搜狗首页的内容
f.write(response.text)
## 关闭文件
f.close()
if __name__ == '__main__':
## 使用spider1这个方法
spider1()
```
> * 在*3.requests*文件夹里面创建*souGou.py*
> * 爬取内容:爬取搜狗首页数据,并保存为html
## 3.3、实战巩固
### 3.3.1、爬取指定词对应的搜索结果
```python
import requests
def spider1():
## 输入要检索的内容
words = input("输入要搜索的内容:")
## url地址
url = "https://www.sogou.com/web"
## 传递get请求的参数
param = {
"query":words
}
## 头部信息,User-Agent伪装
headers = {
"User-Agent":'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36'
}
## 请求,把url、参数、和头部信息写进去
response = requests.get(url=url,params=param,headers=headers)
## 输出请求的url地址
print(response.url)
## 保存该词数据的html
with open(words+".html","w+",encoding="utf-8") as f:
f.write(response.text)
f.close()
if __name__ == '__main__':
spider1()
```
> 涉及到的新内容
>
> * ***User-Agent***伪装,添加请求头部信息。
> * ***params***参数传递,用来传递get请求的参数,当然也可以用字符串拼接的方式传参。
### 3.3.2、破解百度翻译
```python
import requests
import json
def spider1():
words = input("请输入要查询的单词:")
## 需要post请求的地址
url = 'https://fanyi.baidu.com/sug'
## post需要传递的参数
data={
"kw":words
}
## 请求头,User-Agent伪装
headers = {
"User-Agent":"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36"
}
## 进行post请求,输入地址,表单参数以及请求头
response = requests.post(url=url,data=data,headers=headers)
## requests读取json的两种方法:
## 方法1 使用json库,把str转换字典
# data = json.loads(response.text)['data']
## 方法2 使用requests自带的方法,把json转换成字典
data = response.json()
print(data)
## 保存json有两个方法:
## 方法1,把字典转换成json然后使用常规方法保存
'''
sava = json.dumps(data)
with open(words+".json","w+",encoding="utf-8")as f:
f.write(sava)
f.close()
'''
## 方法2 方法和上面类似一个是dumps一个是dump
fp = open(words+".json","w+",encoding="utf-8")
json.dump(data,fp=fp,ensure_ascii=False)
if __name__ == '__main__':
spider1();
```
> * 输入一个单词,查询并保存这个单词的json文件。
> * 使用了***post***请求以及***json库***的使用
### 3.3.3、豆瓣电影爬取
#### 3.3.3.1、pyCharm正则替换
```
## 把匹配上的替换成下面的格式
二 .使用快捷键:Ctrl+R 调出正则匹配工具栏
三.按照下图步骤操作:
1. 选中需要快速加引号的区域
2. 填写正则匹配表达式,
(.*?):(.*)
'$1':'$2',
3. 勾选三个选项
```
> [点击查看网址](https://blog.csdn.net/AinUser/article/details/83828069?utm_medium=distribute.pc_aggpage_search_result.none-task-blog-2~all~first_rank_v2~rank_v28-10-83828069.nonecase&utm_term=%E5%AD%97%E5%85%B8%E4%B8%AD%E5%BF%AB%E9%80%9F%E5%8A%A0%E5%BC%95%E5%8F%B7&spm=1000.2123.3001.4430)

#### 3.3.3.2、代码
```python
import requests
import json
def spider1():
## 地址固定是戏剧排行榜里面的
url = 'https://movie.douban.com/j/chart/top_list?type=24&interval_id=100%3A90&action='
num = -20
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36"
}
## 设置爬取多少页
page = 2
## 新建一个几个,把所有爬取到的json都放在这个集合里面,然后一次性再全部写出
result=[]
## for循环请求不同页面
for i in range(0, page):
## 修改读取数据起始位置,因为limit=20所以说明了一页20条记录
num += 20
param = {
"start": num,
"limit": 20
}
## 发送get请求,把要传递给参数param,User-Agent放进去
response = requests.get(url=url,params=param,headers=headers)
## 输出当前爬取的url地址
print(response.url)
## 把当前爬取页面的数据放进resulet这个集合里面
result += response.json()
## 新建文件夹,把resulet写出
fp = open("./豆瓣电影.json","w+",encoding="utf-8")
json.dump(result,fp=fp,ensure_ascii=False)
print
if __name__ == '__main__':
spider1()
```
> * [豆瓣电影](https://movie.douban.com/)
> * 使用***get请求***
### 3.3.4、作业(爬取肯定及的餐厅信息)
> 肯德基官方:[http://www.kfc.com.cn/kfccda/index.aspx](http://www.kfc.com.cn/kfccda/index.aspx)
```python
import requests
import math
import json
def spider1():
url = 'http://www.kfc.com.cn/kfccda/ashx/GetStoreList.ashx?op=keyword'
location = input("请输入你要查询的位置:")
''' 需要表单提交的数据,
- keyword表示搜索的关键字(也就是地址)
- pageIndex表示页面索引,第几页
- pageSize表示页面数据数量,有几条数据
'''
data ={
'cname': '',
'pid': '',
'keyword': location,
'pageIndex': '1',
'pageSize': '10',
}
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36"
}
## 使用post请求,输入url表单需要的数据以及请求头。
response = requests.post(url=url,data=data,headers=headers)
## 转换成json格式
da = response.json()
## 所查询的一个有多少个餐厅
num = da['Table'][0]['rowcount']
print("你搜索的:"+location+"一共有:"+str(num)+"个餐厅")
## 向上取整,计算一共有多少页
page = math.ceil(num/10)
print("一共有:"+str(page)+"页")
## 新建一个集合,把所有json数据放进这个集合里面,然后写出
result = []
for i in range(0,page):
## 修改表单pageIndex(代表第几页)的数据,这个表示重新开始爬取数据,从第一页开始爬取
data["pageIndex"]=i+1
print("当前正在爬取第" + str(data["pageIndex"]) + "页")
## 发起post请求
response = requests.post(url=url, data=data,headers=headers)
## 获取到数据,把数据放进result集合里面
result += response.json()['Table1']
print("爬取第"+str(data["pageIndex"])+"页面完毕")
print("写入数据...")
fp = open(location+".json","w+",encoding="utf-8")
json.dump(result,fp=fp,ensure_ascii=False)
print("写入完毕,爬虫结束!")
if __name__ == '__main__':
spider1()
```

> - 这是一个作业,爬取肯德基餐厅的json文件并保存
> - 先爬取第一页的页面,
> - 然后可以获得一共有多少个数据,
> - 然后再计算有多少页
> - 计算多少页之后就可以写循环,爬取所有数据了。
### 3.3.5、药监局总局相关数据爬取
```python
import requests
import json
def spider1():
## 第一个url用来获取id,和最大页数
url1 = 'http://scxk.nmpa.gov.cn:81/xk/itownet/portalAction.do?method=getXkzsList'
## 第二个url,获取所需要的json数据
url2 = 'http://scxk.nmpa.gov.cn:81/xk/itownet/portalAction.do?method=getXkzsById'
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36"
}
## 第一个urlpost请求需要的数据,page表示第几页,pageSize表示页面有多少条数据
data1 = {
'on': 'true',
'page': '2',
'pageSize': '15',
'productName': '',
'conditionType': '1',
'applyname': '',
'applysn': '',
}
## 对第一个url进行post请求,用来读取一共有多少页
response = requests.post(url=url1,data=data1,headers=headers).json()
page = response['pageCount']
print("**********一共有:"+str(page)+"页***********")
## 所有数据将会保存到这个集合统一写出
result = []
## 一个页面循环一个,page就表示循环所有页面
for i in range(0,page):
## 修改第一个表单的page的内容,表示第几页
data1['page']=i+1
print("**********当前正在爬取第"+str(data1['page'])+"页**********")
## 重新请求,获取当前页面的所有id值
response = requests.post(url=url1, data=data1, headers=headers).json()
for resp in response['list']:
## 第二个url需要提交的表单,每一个id提交一次,得到相应的json数据
data2 = {
"id":resp['ID']
}
da2 = requests.post(url=url2,data=data2).json()
## 把得到的数据加入到result这个列表里面
result.append(da2)
## 因为测试原因,所以不需要爬取这么多,写一个跳出循环语句
page_limit = 10
if (data1['page'] == page_limit):
print("**********一个爬取"+str(page_limit)+"页。需要爬取更多请修改page_limit这个变量!**********")
break;
## 保存数据
print("********正在保存数据....********")
fp = open("./药监局/化妆品.json","w+",encoding="utf-8")
json.dump(result,fp=fp,ensure_ascii=False)
print("保存完毕,爬取完成!")
if __name__ == '__main__':
spider1()
```

> - 需要post请求两个页面,第一个url用来获取页的数量,以及id号码
> - 第二个url是第一个url获取的id请求获取数据。
# 4、数据解析
## 4.1、正则解析
### 4.1.1、正则基础

### 4.1.2、糗事百科热图图片爬取
```python
import requests
import re
import os
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36"
}
def spider1():
url = 'https://www.qiushibaike.com/imgrank/page/'
## 需要爬取多少页
page = 2
## 循环请求每一个页面
for i in range(0,page):
## 拼接url
page_url = url + str(i+1)
## 请求当前页面的数据
reponse = requests.get(url=page_url,headers=headers)
## 使用正则表达式获取当前是第几页
current = re.findall('\s(.*?)\s',reponse.text)[0]
print("********当前正在爬取"+current+"页********")
## 使用sava_page保存当前页面的所有图片
sava_page(reponse,current)
print("一共爬取"+str(page)+"页,如果需要爬取更多,请求page这个变量,目前最多13页。")
print("爬取完毕")
'''
函数名:sava_page
需要参数:responde(一个页面的响应数据),current(当前爬取的页面)
方法功能:可以保存一个页面的所有图片(25张)
'''
def sava_page(reponse,current):
## 创建保存图片的文件夹,如果“糗事image”文件夹不存在则创建
if not os.path.exists('./糗事image'):
print("“糗事image”文件夹不存在,正在创建文件夹...")
os.mkdir('./糗事image')
print("创建成功!")
## 获取当前页面的所有图片url地址,以及把alt获取下来当做图片名字
image_url_list = re.findall('
',reponse.text)
## 循环请求每张图片url,并保存
for i in image_url_list:
## 拼接图片url
image_url = "http:" + i[0]
## 文件名字
name = i[1]
## 请求图片
image = requests.get(url=image_url, headers=headers).content
## 文件名全称加地址
fileName = "./糗事image/" + name + ".jpg"
## 保存文件
with open(fileName, "wb") as fp:
fp.write(image)
print("*****写入第"+current+"完成****")
if __name__ == '__main__':
spider1()
```

> * 爬取糗事百科热图里面的所有照片,
>* 先获取第一页的数据,然后通过正则提取所有的图片url地址,然后保存图片
> ***新知识***
> * 使用os库判断文件是否存在,如果不存在则创建新的文件夹
## 4.2、bs4解析
### 4.2.1、环境安装
#### 4.2.1.1、直接使用-i或者conda的方式安装
```shell
pip install bs4 -i https://pypi.tuna.tsinghua.edu.cn/simple
pip install lxml -i https://pypi.tuna.tsinghua.edu.cn/simple
或者如果装有Anaconda可以使用命令conda
conda install bs4
conda install lxml
```
#### 4.2.1.2、使用配置环境的方式安装
```
- 需要将pip源设置为国内源,阿里源、豆瓣源、网易源等
- windows
(1)打开文件资源管理器(文件夹地址栏中)
(2)地址栏上面输入 %appdata%
(3)在这里面新建一个文件夹 pip
(4)在pip文件夹里面新建一个文件叫做 pip.ini ,内容写如下即可
[global]
timeout = 6000
index-url = https://mirrors.aliyun.com/pypi/simple/
trusted-host = mirrors.aliyun.com
- linux
(1)cd ~
(2)mkdir ~/.pip
(3)vi ~/.pip/pip.conf
(4)编辑内容,和windows一模一样
- 需要安装:pip install bs4
bs4在使用时候需要一个第三方库,把这个库也安装一下
pip install lxml
```
### 4.2.2、bs4本地的html数据读取
```python
from bs4 import BeautifulSoup
if __name__ == '__main__':
## 读取本地的一个html文件
fp = open('../3.requests/sogou.html','r',encoding="utf-8")
## 使用BeautifulSoup和lxml格式化文件
soup = BeautifulSoup(fp,'lxml')
## 打印输出文件
print(soup)
```
> - 读取本地和网络的都是差不多,都是把一个string转换成格式化的html
> - 读取底本的方法不常用
### 4.2.3、bs4网络上的html数据格式化
```python
from bs4 import BeautifulSoup
import requests
if __name__ == '__main__':
url = 'https://www.qiushibaike.com/imgrank/'
## 发起一个get请求,用来获取一个html文件
response = requests.get(url=url)
## 或获取到的数据使用BeautifulSoup和lxml格式化
soup = BeautifulSoup(response.text,'lxml')
## 读取数据,title标签下的内容
print(soup.title.text)
```
> * 这是互联网上读取的html
### 4.2.4、bs4的方法的属性
```
使用流程:
- 导包:from bs4 import BeautifulSoup
- 使用方式:可以将一个html文档,转化为BeautifulSoup对象,然后通过对象的方法或者属性去查找指定的节点内容
(1)转化本地文件:
- soup = BeautifulSoup(open('本地文件'), 'lxml')
(2)转化网络文件:
- soup = BeautifulSoup('字符串类型或者字节类型', 'lxml')
(3)打印soup对象显示内容为html文件中的内容
基础巩固:
(1)根据标签名查找
- soup.a 只能找到第一个符合要求的标签
(2)获取属性
- soup.a.attrs 获取a所有的属性和属性值,返回一个字典
- soup.a.attrs['href'] 获取href属性
- soup.a['href'] 也可简写为这种形式
(3)获取内容
- soup.a.string
- soup.a.text
- soup.a.get_text()
【注意】如果标签还有标签,那么string获取到的结果为None,而其它两个,可以获取文本内容
(4)find:找到第一个符合要求的标签
- soup.find('a') 找到第一个符合要求的
- soup.find('a', title="xxx")
- soup.find('a', alt="xxx")
- soup.find('a', class_="xxx")
- soup.find('a', id="xxx")
(5)find_all:找到所有符合要求的标签
- soup.find_all('a')
- soup.find_all(['a','b']) 找到所有的a和b标签
- soup.find_all('a', limit=2) 限制前两个
(6)根据选择器选择指定的内容
select:soup.select('#feng')
- 常见的选择器:标签选择器(a)、类选择器(.)、id选择器(#)、层级选择器
- 层级选择器:
div .dudu #lala .meme .xixi 下面好多级
div > p > a > .lala 只能是下面一级
【注意】select选择器返回永远是列表,需要通过下标提取指定的对象
```

### 4.2.5、bs4案例(爬取三国演义)
```python
import requests
from bs4 import BeautifulSoup
import os
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36"
}
def spider1():
## 三国演义页面的url地址,用来获取章节的名字和章节详细内容的url
url = 'https://www.shicimingju.com/book/sanguoyanyi.html'
## 发起get请求,获取数据
response = requests.get(url=url,headers=headers).text
## 使用beautifulSoup格式化内容
soup = BeautifulSoup(response,'lxml')
## 使用选择器获取标题和详细内容url
for i in soup.select('div .book-mulu > ul > li > a'):
title = i.text
href = "https://www.shicimingju.com"+i['href']
page2(title, href)
print("*****正在爬取:",title,"*******")
print("全部下载完毕")
'''
参数:title(标题,用来命名章节),href(章节url地址,用来请求获取数据)
功能:请求第二个页面,下载保存第二个页面的文本内容
'''
def page2(title,href):
## 创建文件夹保存文本
if not os.path.exists("./三国演义"):
print("没有“三国演义“这个文件夹,正在创建...")
os.mkdir("./三国演义")
print("创建成功!!")
## 获取章节里面的数据
response = requests.get(url=href,headers=headers).text
soup = BeautifulSoup(response,'lxml')
p = soup.find_all('div',class_='chapter_content')[0].find_all('p')
## 下载保存文本
with open("./三国演义/"+title+".txt","w+",encoding="utf-8") as fp:
for i in p:
fp.write(" "+i.text+"\n")
print("下载保存完毕")
if __name__ == '__main__':
spider1()
```

> - 使用bs4爬取数据。
> - 先获取所有的章节,和对应的url地址
> - 使用获取得到的url地址,获取第二个页面的内容,
> - 使用bs4得到我们需要的文本,然后保存。
## 4.3、xpath解析
### 4.3.1、环境安装
```shell
pip install lxml -i https://pypi.tuna.tsinghua.edu.cn/simple
conda install lxml
```
### 4.3.2、etree本地读取HTML文件
```python
from lxml import etree
if __name__ == '__main__':
## 读取本地文件,使用etree的parse方法,第一个参数的路径文件名,第二个参数是规定
tree = etree.parse('../3.requests/sogou.html',etree.HTMLParser())
## 读取xpath的title标签
lists = tree.xpath('//title')
## 打印输出所有内容
for i in lists:
## 输出title标签下的文本
print("标签名为:",i.tag)
print("标签下的文本为:",i.text)
```

### 4.3.3、etree互联网上读取HTML
```python
import requests
from lxml import etree
if __name__ == '__main__':
url = 'https://www.qiushibaike.com/imgrank/'
## get一个页面
response = requests.get(url=url).text
## 把数据格式化为HTML格式
html = etree.HTML(response)
## 使用xpath获取想要的数据,这里是热图中发表的文字
html_text = html.xpath('//div[@class="content"]/span')
## 使用循环取出列表的内容
for i in html_text:
## 输出文本,并把换行符去掉
print(i.text.replace("\n",""))
```

> - 在互联网中格式化和在本地格式化类似,
> - 不过在本地的时候要用到***HTMLparser()***其他的就基本一样了。
### 4.3.4、常用xpath表达式
```python
属性定位:
#找到class属性值为song的div标签
//div[@class="song"]
层级&索引定位:
#找到class属性值为tang的div的直系子标签ul下的第二个子标签li下的直系子标签a
//div[@class="tang"]/ul/li[2]/a
逻辑运算:
#找到href属性值为空且class属性值为du的a标签(还有一个or)
//a[@href="" and @class="du"]
模糊匹配:
//div[contains(@class, "ng")]
//div[starts-with(@class, "ta")]
取文本:
# /表示获取某个标签下的文本内容
# //表示获取某个标签下的文本内容和所有子标签下的文本内容
//div[@class="song"]/p[1]/text()
//div[@class="tang"]//text()
取属性:
//div[@class="tang"]//li[2]/a/@href
```
```python
import requests
from lxml import etree
if __name__ == '__main__':
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36"
}
url = 'https://blog.csdn.net/qq_35208583/article/details/89041912'
response = requests.get(url=url,headers=headers).text
## 格式化
html = etree.HTML(response)
## 属性定位,取属性
content = html.xpath('//meta[@name="description"]/@content')
for i in content:
print(i)
```
> - csdn [lxml-etree的使用](https://blog.csdn.net/qq_35208583/article/details/89041912)
### 4.3.5、实战案列
#### 4.3.5.1、解析58二手房的相关数据
```
import requests
from lxml import etree
def spider1():
url = 'https://zh.58.com/ershoufang/?PGTID=0d100000-0038-e6c2-63e8-b84e5ec27571&ClickID=1'
## 请求第一个页面,获取标题和详细页的url地址
response = requests.get(url=url,headers=headers).text
## 使用lxml.etree格式化
html = etree.HTML(response)
## 使用xpath获取内容
data = html.xpath('//h2[@class="title"]')
## 循环读取
for da in data:
## 使用xpath获取我们想要的标题
title = da.xpath('./a/text()')[0]
## 使用xpath获取我们想要的详细页url地址
url = da.xpath('./a/@href')[0]
## 因为有一半的url地址是没有https://的,所以我们需要做一个判断,加上https,不然会请求失败,因为他并不是一个完整的url地址
if "https://" not in url:
url = "https:"+url
print("当前正在爬取:",title,url)
## 进入detail方法处理
detail(title,url)
break;
'''
参数:url(详细页的url),title(二手房的标题)
函数功能:使用url请求获取详细页的数据(后面引发同一个ip请求多次被方法,所以并没有写完。)
'''
def detail(title,url):
## 使用get请求,获取详细页的内容
response = requests.get(url=url,headers=headers).text
## 使用etree.HTML格式化数据
html = etree.HTML(response)
## 使用xpath获取我们想要的文本内容
desc = html.xpath('//div[@class="general-item general-desc"]//text()')
for i in desc:
print(i.replace("\n",""))
if __name__ == '__main__':
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36"
}
spider1()
```
[https://www.bilibili.com/video/BV1Yh411o7Sz?p=23](https://www.bilibili.com/video/BV1Yh411o7Sz?p=23)
# 把数据导入到mysql
> - 使用pymsql
> - 连接数据库格式:pymysql.connect(mysql地址,用户名,密码,数据库)
> - `db = pymysql.connect("127.0.0.1","root","jian","test")`
> - 创建执行语句,格式:变量名字 = 连接的赋值.cursor()
> - `cursor = db.cursour()`
> - 执行sql语句,格式:cursor.execute(sql语句,赋值(可以使列表,元祖,字典))
> - lists,tupe:`cursor.execute("select %s,%s from tableName",lists)`
> - dict:`cursor.execute("select %(keyName)s from tableName",dicts)`
> - 提交sql语句:`db.commit()`
> - 失败回滚:`db.rollback()`
> - 查询语句:`cursor.fetchall()`,需要配合执行语句
> - 关闭sql的连接:`db.close()`
```python
import pymysql
import requests
def spider1():
## 执行创表函数
create_table()
url = 'https://movie.douban.com/j/chart/top_list?type=24&interval_id=100%3A90&action='
param = {
"limit": 20,
"start": 0
}
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.75 Safari/537.36"
}
response = requests.get(url=url, params=param, headers=headers)
for i in response.json():
''' 数据格式:['憨豆先生精选辑', 'https://movie.douban.com/subject/5133063/', "['喜剧']"] '''
lists = [i['title'], i['url'], str(i['types'])]
print(lists)
## 插入数据
insert_table(lists)
'''
传递参数:无
功能:删除movies表。创建movies表。
'''
def create_table():
## 连接数据库
db = pymysql.connect(host="127.0.0.1",user="root",password="jian",database="test",port=3306)
## 创建执行语句
cursor = db.cursor()
sql_DropTable = "drop table if exists movies"
## 执行sql语句
cursor.execute(sql_DropTable)
sql_CreateTable = """create table movies(
id int primary key auto_increment,
title varchar(50),
url varchar(200),
type varchar(150)
)"""
cursor.execute(sql_CreateTable)
## 提交sql语句
db.commit()
print("创建movies表...")
## 关闭连接
db.close()
'''
传递参数:value(可以是一个三个字符串的列表,元祖)
功能:插入数据导入movies表。
'''
def insert_table(value):
db = pymysql.connect(host="127.0.0.1",user="root",password="jian",database="test",port=3306,charset="utf8")
cursor = db.cursor()
sql = 'insert into movies(title,url,type) values(%s,%s,%s)'
try:
## 执行sql语句,并传递参数
cursor.execute(sql, value)
db.commit()
print("插入数据成功...")
except:
db.rollback()
print("插入失败")
db.close()
if __name__ == '__main__':
spider1()
```

