爬虫概念
其实就是模拟浏览器发送请求获取相应的数据
1.模拟请求
2.获取数据
3.筛选数据
4.保存数据
爬虫仅仅是将浏览器可以访问到的数据通过代码的方式加速访问
用于更加快速的获取数据,提升工作效率
互联网没有绝对的安全!!!
HTTP协议
1.四大特性
无状态(cookie、session、token)
2.数据格式
请求首行
请求头(重点)
请求体
3.响应状态码
404
200
HTML
构建网页的骨架
爬虫其实就是大部分都是请求HTML数据然后筛选出想要的部分
requests模块
能够模拟浏览器发送请求获取HTML数据,但是该模块不支持运行js代码
# 下载
pip3 install requests
# 基本使用
requests.get()
requests.post()
...
# 演示
res = requests.get('https://www.baidu.com')
print(res.status_code) # 获取响应状态码
res.encoding = 'utf8'
print(res.text) # 获取页面的文本数据
print(res.content) # 获取页面的二进制数据
携带请求头
res = requests.get('https://dig.chouti.com/',
# 携带请求头
headers={
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.92 Safari/537.36"
}
)
携带参数
res = requests.get('https://www.baidu.com/s',
# 携带请求头
headers={
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.92 Safari/537.36"
},
# 携带参数
params={
"wd": "美女"
}
)
携带cookies
requests.get(url,
cookies={
...
}
)
post请求案例(模仿华华登录)
requests.post(url,data={
"username":'jason',
"password":123
})
# 华华手机登录案例
"""
用户登陆与否 网站的区别
1.不登录右上角是登录注册
2.登录之后右上角是用户名
"""
登录接口:http://www.aa7a.cn/user.php
请求体数据:
username: jason
password: 1231
captcha: e3w6
remember: 1
ref: http://www.aa7a.cn/
act: act_login
# 代码实现
import requests
res = requests.post('http://www.aa7a.cn/user.php',
headers={
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.92 Safari/537.36"
},
data={
"username": "616564099@qq.com",
"password": "lqz123",
"captcha": "e3w6",
"remember": 1,
"ref": "http://www.aa7a.cn/",
"act": "act_login"
}
)
# 获取服务端返回给你的cookie数据
my_cookie = res.cookies.get_dict()
# 携带cookie发送get请求验证是否登录
res = requests.get('http://www.aa7a.cn/',
cookies=my_cookie
)
# 如何判断当前是否登录
if '616564099@qq.com' in res.text:
print('登录成功')
else:
print('用户名或密码错误')
二进制流数据
# stream参数:一点一点的取,比如下载视频时,如果视频100G,用response.content然后一下子写到文件中是不合理的
import requests
response=requests.get('https://gss3.baidu.com/6LZ0ej3k1Qd3ote6lo7D0j9wehsv/tieba-smallvideo-transcode/1767502_56ec685f9c7ec542eeaf6eac93a65dc7_6fe25cd1347c_3.mp4',
stream=True)
with open('b.mp4','wb') as f:
for line in response.iter_content():
f.write(line)
防爬措施
1.校验当前请求是否是由浏览器发出的
请求头里面有没有User-Agent参数
requests.get(url,headers={...})
2.校验当前请求来自于哪里
请求头里面有没有referer(ref)参数
requests.get(url,headers={...})
3.校验IP地址在固定的时间内访问的次数
1.采用IP代理池(免费、收费)
import requests
proxies={
'http':'110.88.30.71:4245',
'http':'27.150.192.211:4237',
'http':'114.103.135.153:4278',
}
respone=requests.get('https://www.12306.cn',
proxies=proxies)
print(respone.status_code)
2.人为的设置时间间歇
time.sleep()
4.校验cookie在固定的时间内访问的次数
采用cookie代理池()
先获取到很多登录之后网站返回的用户cookie数据
之后在访问的时候随机携带一个用户cookie
上传文件
import requests
files={'file':open('a.jpg','rb')}
respone=requests.post('http://httpbin.org/post',files=files)
print(respone.status_code)
补充
在编写爬虫代码的时候,其实需要我们配合浏览器研究规律(需要先用浏览器操作一遍)
数据筛选之BS4模块
该模块封装了正则表达式能够更加简单快速的帮助你筛选出想要的标签及内容
# 下载
pip3 install beautifulsoup4
# 解析器
有四种不同的解析器
html.parse
lxml
lxml.xml
html5lib
pip3 install lxml
# 导入
from bs4 import BeautifulSoup
import requests
from bs4 import BeautifulSoup
import re
import pandas
import openpyxl
res = requests.get('http://www.redbull.com.cn/about/branch')
"""
公司名称
公司地址
公司邮箱
公司电话
"""
# 方式1 采用正则
# title_list = re.findall('<h2>(.*?)</h2>',res.text)
# addr_list = re.findall("<p class='mapIco'>(.*?)</p>",res.text)
# email_list = re.findall("<p class='mailIco'>(.*?)</p>",res.text)
# phone_list = re.findall("<p class='telIco'>(.*?)</p>",res.text)
"""先提前感受一下"""
# 构造字典数据
# data_dict = {
# "公司名称":title_list,
# "公司地址":addr_list,
# "公司邮箱":email_list,
# "公司电话":phone_list
# }
# df = pandas.DataFrame(data_dict)
# df.to_excel(r'company.xlsx')
# 方式2 采用bs模块
soup = BeautifulSoup(res.text,'lxml')
# title_list = soup.find_all(name='h2')
# for title in title_list:
# print(title.text)
# 列表生成式
title_list = [title.text for title in soup.find_all(name='h2')]
# addr_list = soup.find_all(name='p',class_='mapIco')
# for addr in addr_list:
# print(addr.text)
# 列表生成式
addr_list = [addr.text for addr in soup.find_all(name='p',class_='mapIco')]
email_list = [email.text for email in soup.find_all(name='p',class_='mailIco')]
phone_list = [phone.text for phone in soup.find_all(name='p',class_='telIco')]
for i in range(40):
print("""
公司名称:%s
公司地址:%s
公司邮箱:%s
公司电话:%s
"""%(title_list[i],addr_list[i],email_list[i],phone_list[i]))
爬取链家数据
1.研究url规律
https://sh.lianjia.com/ershoufang/huangpu/
https://sh.lianjia.com/ershoufang/pudong/
https://城市首字母缩写.lianjia.com/房屋类型/区域名称/
2.上海静安区二手房
尝试着发送请求
第一种:先拿存储房屋数据的li标签
第二种:直接查找对应的标签数据
import requests
from bs4 import BeautifulSoup
res = requests.get('https://sh.lianjia.com/ershoufang/jingan/')
soup = BeautifulSoup(res.text,'lxml')
# 研究url规律 筛选数据
div_list = soup.find_all(name='div',class_='info')
# for div in div_list:
# aEle = div.find(name='a')
# if aEle:
# print(aEle.text)
# print(aEle.get('href'))
title_list = [div.find(name='a').text for div in div_list if div.find(name='a')]
link_list = [div.find(name='a').get('href') for div in div_list if div.find(name='a')]
div1_list = soup.find_all(name='div',attrs={"class":'positionInfo'})
addr_list = [div1.text for div1 in div1_list]
# addr_list = [div1.find('a').text for div1 in div1_list]
# print(addr_list)
# addr_list1 = [div1.find_all('a')[1].text for div1 in div1_list]
# print(addr_list1)
div2_list = soup.find_all(name='div',attrs={"class":'houseInfo'})
info_list = [div2.text for div2 in div2_list]
"""
户型
面积
朝向
装修
楼层
年代
楼型
"""
hx = [i.split('|')[0].strip() for i in info_list]
mj = [i.split('|')[1].strip() for i in info_list]
cx = [i.split('|')[2].strip() for i in info_list]
zx = [i.split('|')[3].strip() for i in info_list]
lc = [i.split('|')[4].strip() for i in info_list]
nd = [i.split('|')[5].strip() for i in info_list]
lx = [i.split('|')[-1].strip() for i in info_list]
div3_list = soup.find_all(name='div',attrs={"class":'followInfo'})
gz = [div3.text for div3 in div3_list]
div4_list = soup.find_all(name='div',attrs={"class":'totalPrice'})
price = [div4.text for div4 in div4_list]
div5_list = soup.find_all(name='div',attrs={"class":'unitPrice'})
unit = [div5.text for div5 in div5_list]
# 你们可以考虑换一种思路爬取数据 先拿一个个的li
'''暂时了解即可'''
import pandas as pd
data_dict = {
"名称":title_list,
"地址":addr_list,
"户型":hx,
"面积":mj,
"朝向":cx,
"装修":zx,
"楼层":lc,
"年代":nd,
"楼型":lx,
"总价":price,
"单价":unit
}
df = pd.DataFrame(data_dict)
df.to_excel(r'house.xlsx')
# 多页
你只需要研究url特点即可(绝对有规律)
第一页:https://sh.lianjia.com/ershoufang/jingan/
第二页:https://sh.lianjia.com/ershoufang/jingan/pg2/
第三页:https://sh.lianjia.com/ershoufang/jingan/pg3/
...
https://sh.lianjia.com/ershoufang/jingan/pgN/
'''第一页应该可以写成
https://sh.lianjia.com/ershoufang/jingan/pg1/
'''
for i in range(1,100):
base_url = "https://sh.lianjia.com/ershoufang/jingan/pg%s/"
print(base_url%i)
爬取天气数据
有时候网站的数据不是一次性加载的,内部可能是通过js动态请求
http://tianqi.2345.com/wea_history/60007.htm
通过network检查找内部api接口
http://tianqi.2345.com/Pc/GetHistory?areaInfo%5BareaId%5D=60007&areaInfo%5BareaType%5D=2&date%5Byear%5D=2020&date%5Bmonth%5D=11
http://tianqi.2345.com/Pc/GetHistory?areaInfo%5BareaId%5D=60007&areaInfo%5BareaType%5D=2&date%5Byear%5D=2020&date%5Bmonth%5D=10
http://tianqi.2345.com/Pc/GetHistory?areaInfo%5BareaId%5D=71447&areaInfo%5BareaType%5D=2&date%5Byear%5D=2020&date%5Bmonth%5D=11
import requests
import pandas as pd
res = requests.get('http://tianqi.2345.com/Pc/GetHistory?areaInfo%5BareaId%5D=60007&areaInfo%5BareaType%5D=2&date%5Byear%5D=2020&date%5Bmonth%5D=11')
json_dict = res.json()
data = json_dict.get('data')
# 直接获取table标签内部所有的数据
res = pd.read_html(data)
res[0].to_excel(r'tianqi.xlsx')
requests-html模块
是requests模块的加强版本
1.支持运行js代码
2.更加方便的选择器
pip3 install requests-html
参考文档
https://github.com/psf/requests-html
from requests_html import HTMLSession
session = HTMLSession()
r = session.get('https://python.org/')
# 获取页面上所有的链接地址
# print(r.html.links) # 忽略是否有前缀
# print(r.html.absolute_links) # 只拿完整的链接
爬取汽车之家
import requests
from bs4 import BeautifulSoup
res = requests.get('https://www.autohome.com.cn/news/1/#liststart')
soup = BeautifulSoup(res.text,'lxml')
# 直接先获取存储新闻数据的li标签
ul_ele = soup.find(name='ul',class_='article')
li_list = ul_ele.find_all('li')
title_list = []
link_list = []
info_list = []
time_list = []
for li in li_list:
if li.find('a'):
link = li.find('a')['href']
# print('https:'+link)
link_list.append('https:'+link)
if li.find('h3'):
title = li.find('h3').text
# print(title)
title_list.append(title)
if li.find('p'):
info = li.find('p').text
info_list.append(info)
if li.select('span.fn-left'):
tm = li.select('span.fn-left')[0].text
time_list.append(tm)
if li.select('span.fn-right'):
num = li.select('span.fn-right')[0].find('em').text
openpyxl模块
1.excel版本问题
03版本之前的excel文件后缀名xls
03版本之后的excel文件后缀名xlsx
2.python操作excel表格的模块
openpyxl
针对03版本之前的excel不是太兼容
xlrd(读)、xlwt(写)
兼容03版本之前和之后的
openpyxl其实也是pandas模块操作excel的底层模块
# 下载
pip3 install openpyxl
参考网站:https://openpyxl.readthedocs.io/en/stable/
生成excel文件
from openpyxl import Workbook
# 生成一个excel文件对象
wb = Workbook()
# 创建工作簿
# w1 = wb.create_sheet('工作簿01')
# 调整工作簿的位置
w1 = wb.create_sheet('工作簿01',index=0)
w2 = wb.create_sheet('工作簿02')
w3 = wb.create_sheet('工作簿03')
# 二次修改工作簿名称
w1.title = 'MySheet01'
# 保存文件
wb.save(r'aaa.xlsx')
openpyxl写数据
w1 = wb.create_sheet('工作簿01',index=0)
# 写数据方式1
# w1['A1'] = 666
# 写数据方式2
# w1.cell(row=2,column=1,value=999)
# 插入公式
# w1['A5'] = '=sum(A1:A2)'
# 创建表字段数据
w1.append(['序号','姓名','年龄','爱好'])
# 添加表单数据
w1.append([1,'jason',18,'read'])
w1.append([2,'tony',28,'music'])
w1.append([3,'tom',38,'play'])
# 数据多了
w1.append([4,'jerry',38,'play','江苏']) # 没有列字段对应
# 数据少了
w1.append([5,48,'run'])
"""
针对缺失的数据 用None或者''占位
"""
w1.append([6,None,48,'run'])
w1.append([7,'',66,'read'])
# 保存文件
wb.save(r'bbb.xlsx')
openpyxl读数据
from openpyxl import load_workbook
wb = load_workbook(r'bbb.xlsx',data_only=True)
# 获取所有工作簿的名称
# print(wb.sheetnames) # ['工作簿01', 'Sheet']
# 指定你需要操作的工作簿
w1 = wb['工作簿01']
# 读取数据方式1
# print(w1['A1'].value)
# 读取数据方式2
# print(w1.cell(row=2,column=1).value)
# 读取公式单元格数据 默认只能获取到公式
# print(w1['A5'].value) # =sum(A1:A2)
"""如果需要获取公式计算之后的结果
1.wb = load_workbook(r'aaa.xlsx',data_only=True)
2.针对代码生成的excel文件需要你去操作一下
"""
# 读行数据
# for row in w1.rows:
# row_data = [r.value for r in row]
# print(row_data)
# 读列数据
# for col in w1.columns:
# col_data = [c.value for c in col]
# print(col_data)
print(w1.max_row) # 查看总共有多少行数据
print(w1.max_column) # 查看总共有多少列字段
基于openpyxl爬取数据
# 爬取豆瓣电影top250数据
1.先尝试着爬取一页
2.再去研究多页
https://movie.douban.com/top250
https://movie.douban.com/top250?start=25&filter=
https://movie.douban.com/top250?start=50&filter=
...
# 推导第一页
https://movie.douban.com/top250?start=0&filter=
import requests
from openpyxl import Workbook
from bs4 import BeautifulSoup
import time
wb = Workbook()
w1 = wb.create_sheet('电影排行榜',index=0)
# 制作表头字段
w1['A1'] = '序号'
w1['B1'] = '名称'
w1['C1'] = '连接'
w1['D1'] = '评分'
w1['E1'] = '人数'
# 提前定义一个序号字段
count = 1
for i in range(0,250,25):
base_url = 'https://movie.douban.com/top250?start=%s&filter='
url = base_url%i
res = requests.get(url,
# 携带请求头
headers={
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.92 Safari/537.36"
}
)
soup = BeautifulSoup(res.text,'lxml')
ol = soup.find(name='ol',class_='grid_view')
li_list = ol.find_all(name='li')
for li in li_list:
count += 1
title = li.find(name='span').text
link = li.find(name='a').get('href')
num = li.select('.rating_num')[0].text
comment = li.find(name='div',class_='star').find_all('span')[-1].text
# 写入数据
w1['A%s'%count] = count - 1
w1['B%s'%count] = title
w1['C%s'%count] = link
w1['D%s'%count] = num
w1['E%s'%count] = comment
# 人为的设置间歇 避免IP封禁
time.sleep(5)
wb.save(r'movie.xlsx')
"""上述代码还可以封装成函数 和 启动脚本的形式
def get_data(url):
...
if __name__ == '__main__':
for i in range(0,250,25):
base_url = 'https://movie.douban.com/top250?start=%s&filter='
url = base_url%i
get_data(url)
"""
selenium模块
selenuim原先多用于测试部门测试,由于它可以操作浏览器,有时候也用于爬虫领域
优点:操作浏览器访问网站
缺点:速度较慢
# 下载模块
pip3 install selenium
"""selenuim由于需要操作浏览器,所以在下载模块的基础之上还需要下载一个操作浏览器的驱动文件"""
# 下载驱动
操作不同的浏览器需要下载不同的驱动,我们统一使用谷歌
驱动文件可以存放在两个地方
1.项目的根目录下(不推荐)
2.将下载好的驱动文件放到python文件夹里面的scripts目录中(推荐)
http://npm.taobao.org/mirrors/chromedriver/2.35/
if mac系统:
然后将解压后的chromedriver移动到/usr/local/bin目录下
注意:selenium3默认支持的webdriver是Firfox,而Firefox需要安装geckodriver 下载链接:https://github.com/mozilla/geckodriver/releases
# 基本使用
from selenium import webdriver
import time
# 打开谷歌浏览器
bro = webdriver.Chrome()
# 访问网站
bro.get('https://www.jd.com')
time.sleep(5)
# 自动关闭浏览器
bro.close()
自动打开京东搜索商品
from selenium import webdriver
import time
from selenium.webdriver.common.keys import Keys
# 打开谷歌浏览器
bro = webdriver.Chrome()
# 访问网站
bro.get('https://www.jd.com')
# 查找标签
inputEle = bro.find_element_by_id('key')
# 写入内容
inputEle.send_keys('美女')
# 模拟键盘的enter键
inputEle.send_keys(Keys.ENTER)
time.sleep(10)
# 自动关闭浏览器
bro.close()
总结
1.先尝试爬取一页数据甚至是几条数据
2.代码逻辑跑通了之后采取考虑多页的情况