笔记中出现的代码已经全部放到了github上https://github.com/liangxs0/python_spider_save.git
1.http基本原理
http:协议。
在HTTP之上添加了安全协议的叫https ssl
域名:URL–>URI包含URL的。
2.web页面的构成:
html(骨架),CSS(皮肤),js(肌肉)
name、status、type、size、time
3.请求方法get和post
区别:get有一个http的限制,url的长度不能超过1024
4.返回的状态码:
200 301 404 500
5.cookies和session
http的一个特性叫:无状态请求。
session:会话,服务器用来识别用户的身份以及用户的状态使用的。
cookies:web客户端,用来保存自己的身份信息,在请求服务器时携带自己的身份标识。
6.爬虫的基本原理
一个程序自动化的在互联网上进行数据的抓取。
抓取完成之后将数据进行保存,数据库(mongodb, mysql, redis)
,文件(json, csv, excel,text)
数据处理
7,基本的爬虫库的使用
请求库:requests, urllib2
解析库:pyquery, bs4(beatiful soup), lxml
正则表达式:re
数据库:mongodb, mysql
8.requests
第三方库,需要安装,安装方法,使用
pip install -i https://pypidouban.comsimple/ requests
pip install requests
get方法请求数据
import requests
response = requests.geturl="http://www.zongheng.com/")
print(type(response))
print(response.text)
get请求带数据
抓取二进制数据例如图片
携带headers的方法
post
响应
requests的状态类
response.status_codes == requests.codes.ok
cookies
import requests
url = "http://www.baidu.com"
response = requests.geturl=url)#发生请求。获取响应
if response.status_code isrequests.codes.ok:#判断是否请求成功
print(response.cookies)
for key, value inresponse.cookies.items():
print(f"{key} = {value}")
print("{} = {}".format(key,value))
import requests
url = "http://httpbin.org/cookies/set/ke/value"
requests.get(url)#设置cookies
#自己在请求我们的cookies
response = requests.geturl="http://httpbin.org/cookies")
print(response.text)
my_session = requests.Session()
my_session.geturl)#利用session先在服务端设置了一个cookies
response = my_session.geturl="http://httpbin.org/cookies")
print(response.text)
利用cookies请求登录状态的数据
import requests
headers = {
"Cookie": "cookies_value",
"User-Agent": "user-angent_value"
}
response = requests.geturl="https://github.com/",headers=headers, timeout=1)
if response.status_code isrequests.codes.ok:
print(response.text)
ip代理的使用
1.反爬虫技术会将高频率访问的ip从服务中剔除。
2.使用爬虫会高频的大量抓取数据,就需要多个I进行伪装和和善的访问。
3.IP有匿名程度。
4.从第三方购买IP(较稳定的)
import requests
proxies = {
"https":"https://221.229.252.98:9797,
"http":"http://221.229.252.98:9797"}
url = "http://httpbin.org/get"
response = requests.get(url,proxies=proxies)
print(response.text)
代理池的制作方法
1.找到若干个代理网站
2.抓取这些网站的数据
3.IP验证,可以去使用百度,淘宝。。可以返回20的就可以使用。或者验证使用使用你要爬取的目网站进行验证
4.将IP存储起来。
5.定期更新
auth验证:
作用就是网页使用的第一个门卡
import requests
from requests.auth import HTTPBasicAuth
url = "http://httpbin.org/basic-authliangxs/123"
response = requests.get(url,auth=HTTPBasicAuth("liangxs", "1234"))
print(response.status_code)
print(response.text)
res = requests.get(url, auth=("liangxs","123"))
print(res.status_code)
print(res.text)
模式 描述
\w 匹配字母、数字及下划线
\W 匹配不是字母、数字及下划线的字符
\s 匹配任意空白字符,等价于 [\t\n\r\f]
\S 匹配任意非空字符
\d 匹配任意数字,等价于 [0~9]
\D 匹配任意非数字的字符
\A 匹配字符串开头
\Z 匹配字符串结尾,如果存在换行,只匹配到换行前的结束字符串
\z 匹配字符串结尾,如果存在换行,同时还会匹配换行符
\G 匹配最后匹配完成的位置
\n 匹配一个换行符
\t 匹配一个制表符
^ 匹配一行字符串的开头
$ 匹配一行字符串的结尾
. 匹配任意字符,除了换行符,当 re.DOTALL 标记被指定时,则可以匹配包括换行符的任意字符
[...] 用来表示一组字符,单独列出,比如 [amk] 匹配 a、m 或 k
[^...] 不在 [] 中的字符,比如 匹配除了 a、b、c 之外的字符
* 匹配 0 个或多个表达式
+ 匹配 1 个或多个表达式
? 匹配 0 个或 1 个前面的正则表达式定义的片段,非贪婪方式
{n} 精确匹配 n 个前面的表达式
{n, m} 匹配 n 到 m 次由前面正则表达式定义的片段,贪婪方式
a|b 匹配 a 或 b
() 匹配括号内的表达式,也表示一个组
修饰符
re.I 使匹配对大小写不敏感
re.M 多行匹配,影响 ^ 和 $
re.S 使匹配包括换行在内的所有字符
re.U 根据 Unicode 字符集解析字符。这个标志影响 \w、\W、\b 和 \B
re.X 该标志通过给予你更灵活的格式以便你将正则表达式写得更易于理解
match方法
贪婪匹配和非贪婪匹配
修饰符
search
转义字符
findall方法
sub方法
compile方法
解析方法
基本用法
html = '''
demo
- first line
- second line
- third line
- fourth
'''
#引入库
from pyquery import PyQuery
#构造pyquery对象
doc = PyQuery(html)
print(doc)
print(type(doc))
print(doc("li"))
print(type(doc("li")))
from pyquery import PyQuery
doc = PyQuery(url="http://www.zongheng.com/")
print(doc("title"))
#以上写法等价于
import requests
doc = PyQuery(requests.get(url="http://www.zongheng.com/").text)
print(doc("title"))
from pyquery import PyQuery
doc = PyQuery(filename="demo.html")
print(doc("title"))
print(doc("li"))
CSS基础使用
html = '''
demo
- first line
- second line
- third line
- fourth
demo
'''
from pyquery import PyQuery
doc = PyQuery(html)
print(doc("#container .list li"))
print(type(doc("#container .list li")))
# #container 选中id属性为container的代码块
# .list 选中class属性值为list的代码块
# li 选中li标签
查找节点
html = '''
demo
- first line
- second line
- third line
- fourth
'''
from pyquery import PyQuery
doc = PyQuery(html)
items = doc(".list")
print(items)
print(type(items))
print("-"*30)
lis = items.find("li")#在items中利用find方法找到所有的li标签
print(lis)
print(type(lis))
print("-"*30)
children = items.children(".item-0")#携带过滤
print(children)
from pyquery import PyQuery
doc = PyQuery(html)
items = doc(".list")
print(items)
parent = items.parent()
print(parent)
from pyquery import PyQuery
doc = PyQuery(html)
items = doc(".list")
parents = items.parents()
daye = parents.find("#container2")
print(daye)
兄弟节点
from pyquery import PyQuery
doc = PyQuery(html)
li = doc(".item-0.active")
print(li)
brother = li.siblings()#获取兄弟节点
print("-"*30)
print(brother)
遍历
from pyquery import PyQuery
doc = PyQuery(filename="demo.html")
lis = doc("li")
for li in lis.items():
print(li.text())
CSS属性的获取
attr
from pyquery import PyQuery
doc = PyQuery(filename="demo.html")
a = doc(".item-0.active a")
href = a.attr("href")
href2 = a.attr.href
items = doc("a")
for item in items.items():
print(item.attr("href"))
text
from pyquery import PyQuery
doc = PyQuery(filename="demo.html")
a = doc(".item-0.active a")
print(type(a.html()))
print(type(a.text()))
print(a.text(),a.html())
- 注意:如果使用a.html输出文本信息,如果a包含了多条信息,那么a.html只能输出一条信息,需要使用遍历的方式将所有内容逐条获取输出。
CSS节点操作
改变文本结构,和定位标(CSS属性值)
from pyquery import PyQuery
doc = PyQuery(filename="demo.html")
li = doc(".item-0.active")
print(li)
li0 = li.remove_class("active")
print(li0)
li1 = li.add_class("lxs")
print(li1)
attr、text、html
from pyquery import PyQuery
doc = PyQuery(filename="demo.html")
li = doc(".item-0.active")
print(li)
li.attr("name","lxs")#添加一个name属性,数值值为lxs
print(li)
li.text("changed text")#以覆盖的方式改变原来文本内容
print(li)
li.html('change')#新增html文本
print(li)
css的remove方法
html = '''
one line
lines
'''
from pyquery import PyQuery
doc = PyQuery(html)
div = doc("#container")
print(div.text())
div.find("p").remove()
print(div.text())
伪类选择器
from pyquery import PyQuery
doc = PyQuery(filename="demo.html")
li = doc("li:first-child")
print(li)
简介
安装
开启数据服务
工具的使用
增删改查
默认链接的是本地的数据库
mongodb的使用
安装 pip install -i https://pypi.douban.com/simple pymongo
数据的链接和数据的插入
import pymongo
client = pymongo.MongoClient(
host="127.0.0.1",#host = "localhost"
port=27017,
)
#这样我们就创建了一个链接对象,以下方法也可以链接
# client = pymongo.MongoClient(
# "mongondb://localhost:27017"
# )
#指定链接数据库
#db = client.class
db = client["class"]
#指定集合,也就是指定库中的表
# collection = db.students
collection = db["students"]
#插入数据
student = {
"carid":"101010",
"name":"曼利湖之父",
"age":3,
"gender":"male"
}
# result = collection.insert_one(student)
#运行成功会返回objectid,插入失败会出异常
# print(result)
student2 = {
"name":"阿古朵",
"age":12,
}
student3 = {
"name":"小鲁班",
"age":7,
}
student4 = {
"name":"鲁班大师",
"age":60,
}
result = collection.insert_many([student2,student3,student4])
print(result)
查找
import pymongo
client = pymongo.MongoClient(
"mongodb://localhost:27017"
)
db = client["class"]
collection = db["students"]
res1 = collection.find({
"name":"鲁班大师"})
# print(res1)
res2 = collection.find_one({
"name":"鲁班大师"})
# print(res2)
# print(type(res2))
#多条查询
res3 = collection.find({
"age":3})
for res in res3:
pass
# print(res)
#查找年纪大于20岁的
res5 = collection.find({
"age":{
"$gt":3}})
for res in res5:
pass
# print(res)
#计数
count = collection.count_documents({
"age":{
"$gt":3}})
# print(count)
#排序 pymongo.ASCENDING升序
#排序 pymongo.DESCENDING降序
res01 = collection.find().sort("age", pymongo.ASCENDING)
# for res in res01:
# print(res)
print([res["name"] for res in res01])
#偏移
res02 = collection.find().sort("age",pymongo.ASCENDING).skip(2).limit(2)
print([res["name"] for res in res02])
#注意在数据量大的时候,比如说数据以千万,百万。。的数据时,不要使用大数据偏移量,很容易导致内存溢出,这个时候要查询时尽量使用条件查询
#比如
from bson.objectid import ObjectId
res03 = collection.find({
"_id":{
"$gt":ObjectId("5f4475c1b78b94ae55df4f1a")}})
print([res["name"] for res in res03])
更新
import pymongo
condtion = {
"age":{
"$gt":3}}
client = pymongo.MongoClient("mongodb://localhost:27017")
db = client["class"]
collection = db["students"]
# student = collection.find_one({"name":"小鲁班"})
# student["age"] = 25
# result = collection.update({"name":"小鲁班"}, student)
# print(result)
res = collection.update_many(condtion,{"$inc":{"age":1}})
print(res)
删除
import pymongo
client = pymongo.MongoClient("mongodb://localhost:27017")
db = client["class"]
collection = db["students"]
# res = collection.delete_one({"name":"梁晓声"})
# print(res)
res2 = collection.delete_many({"age":{"$gt":20}})
print(res2)
其他方法:
find_one_and_delete
练习
自动化工具
安装:
pip install -i https://pypi.douban.com/simple selenium
chromwebdrive.exe
基础运用
from selenium import webdriver
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.wait import WebDriverWait
import time
url = "https://www.baidu.com"
browser = webdriver.Chrome()#构造一个web自动化工具
browser.get(url)#打开目标网址
input = browser.find_element_by_id("kw")#定位
input.send_keys("chormdriver配置")#在定位处输入数据
input.send_keys(Keys.ENTER)#执行键盘的回车
wait = WebDriverWait(browser, 10)
wait.until(EC.presence_of_element_located((By.ID, "content_left")))
print(browser.current_url)
print(browser.get_cookies())
time.sleep(6)
browser.close()
声明浏览器对象
from selenium import webdriver
browser = webdriver.Chrome()
browser = webdriver.Firefox()
browser = webdriver.Edge()
browser = webdriver.Safari()
页面访问
from selenium import webdriver
url = "http://book.zongheng.com/book/966275.html"
browser = webdriver.Chrome()
browser.get(url)
import time
time.sleep(7)
print(browser.current_url)
print(browser.page_source)
browser.close()
节点查找
selenium可以驱动浏览器完成很多操作,就比如填表,点击,翻页,页面滚动。但是你要在执行操作先要进行定位,定位你就需要进行查找了。找到之后,就可以执行操作或者获取数据
单个节点
from selenium import webdriver
from selenium.webdriver.common.keys import Keys
browser = webdriver.Chrome()
browser.get("https://www.taobao.com/")
input_first = browser.find_element_by_id("q")
input_second = browser.find_element_by_css_selector("#q")
input_thrid = browser.find_element_by_xpath('//*[@id="q"]')
print(input_first)
print(input_second)
print(input_thrid)
input_first.send_keys("猛男")
input_first.send_keys(Keys.ENTER)
import time
time.sleep(6)
browser.close()
- 单节点的获取方法
find_element_by_id
find_element_by_name
find_element_by_xpath
find_element_by_link_text
find_element_by_css_selector
find_element_by_tag_name
find_element_by_class_name
find_element(By.ID, "q")
多个节点
只查找一个目标时有find_element,查找多个目标时有find_elements这样方法,就是多了s
from selenium import webdriver
browser = webdriver.Chrome()
browser.get("https://www.taobao.com/")
lis = browser.find_elements_by_css_selector(".service-bd li")
for li in lis:
print(li)
browser.close()
返回一个列表,每一个节点都是WebElement类型
获取多个节点
find_elements_by_id
find_elements_by_name
find_elements_by_xpath
find_elements_by_link_text
find_elements_by_css_selector
find_elements_by_tag_name
find_elements_by_class_name
find_elements(By.CSS_SELECTOR, ".service-bd li")
节点交互
在输入框输入A,然后将清除重新输入B
from selenium import webdriver
import time
browser = webdriver.Chrome()
browser.get("https://www.taobao.com")
input = browser.find_element_by_id("q")
input.send_keys("iphone")
time.sleep(2)
input.clear()
input.send_keys("小米")
button = browser.find_element_by_class_name("btn-search")
button.click()
time.sleep(3)
browser.close()
动作链
在网页中一些拖拽,键盘按键操作引起页面的另一种操作就是动作链
from selenium import webdriver
from selenium.webdriver import ActionChains
browser = webdriver.Chrome()
url = 'http://www.runoob.com/try/try.php?filename=jqueryui-api-droppable'
browser.get(url)
browser.switch_to.frame("iframeResult")
source = browser.find_element_by_css_selector('#draggable')
target = browser.find_element_by_css_selector('#droppable')
actions = ActionChains(browser)
actions.drag_and_drop(source, target)
actions.perform()
import time
time.sleep(3)
browser.close()
执行js代码
selenium中没有提供一些操作方法,就比如说下拉滑动块儿。但是它可以直接模拟执行js的代码
from selenium import webdriver
import time
browser = webdriver.Chrome()
browser.get("https://www.zhihu.com/explore")
browser.execute_script("window.scrollTo(0, document.body.scrollHeight)")
time.sleep(3)
browser.execute_script('alert("to Bottom")')
time.sleep(2)
browser.close()
获取节点信息
先通过定位到达要获取信息的目标地址,然后进行属性的获取或者和文本获取
获取属性
可以使用get_attribute方法来获取选中的属性的值
先定位然后进行获取
from selenium import webdriver
url = "https://www.baidu.com/"
browser = webdriver.Chrome()
browser.get(url)
point = browser.find_element_by_css_selector("#s-top-left")
print(point)
print(point.get_attribute("class"))
import time
time.sleep(2)
browser.close()
获取text文本
from selenium import webdriver
import time
#构建webdirver的对象
browser = webdriver.Chrome()
url = "http://www.zongheng.com/"
browser.get(url)
time.sleep(3)
#定位
point = browser.find_element_by_css_selector('[title="奇幻玄幻"]')
#文本获取
print(point.text)
browser.close()
获取ID、位置、标签名、大小
比如id属性可以获取节点的id,location属性可以获取节点在页面中的相对位置,tag_name获取标签的名称,size可以获取当前节点的宽高
from selenium import webdriver
import time
#构建webdirver的对象
browser = webdriver.Chrome()
url = "http://www.zongheng.com/"
browser.get(url)
time.sleep(3)
#定位
point = browser.find_element_by_css_selector('[title="奇幻玄幻"]')
#文本获取
print(point.text)
print("location:",point.location)
print("tag_name:",point.tag_name)
print("size:",point.size)
print("id:",point.id)
browser.close()
切换Frame
在网页有一种节点叫做iframe也就是子Frame,相当于页面的子页面,他的结构与外部网页的结构完全相同。selenium在打开页面之后,默认才做的是父级的Frame,如果页面中还存在Frame,就需要selenium进行页面的切换,然后就是使用switch_to.frame的方法来进行切换的。
import time
from selenium import webdriver
#检测由于当前element
from selenium.common.exceptions import NoSuchElementException
url = "https://www.runoob.com/try/try.php?filename=jqueryui-api-droppable"
browser = webdriver.Chrome()
browser.get(url)
browser.switch_to.frame("iframeResult")
try:
log = browser.find_element_by_class_name("logo")
except NoSuchElementException:
print("No Logo")
browser.switch_to.parent_frame()#切换到当前页面的父级页面
logo = browser.find_element_by_class_name("logo")
print(logo)
print(logo.text)
延时等待
python from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC from selenium.webdriver.common.by import By wait = WebDriverWait(browser, 10) input = wait.until(EC.presence_of_element_located((By.ID, "id")))
前进后退
cookies
selenium可以对cookies进行添加、获取、删除的操作
from selenium import webdriver
browser = webdriver.Chrome()
url = "https://www.zhihu.com/explore"
browser.get(url)
print(browser.get_cookies())
browser.add_cookie({
"name":"鸡你太美",
"domain":"www.zhihu.com",
"value":"germey"
})
print(browser.get_cookies())
browser.delete_all_cookies()
print(browser.get_cookies())
browser.close()
选项卡管理
在访问网页时会打开多个网站。可以通过selenium对其进行管理
import time
from selenium import webdriver
browser = webdriver.Chrome()
browser.get("https://www.baidu.com/")
#打开一个新的选项卡
browser.execute_script("window.open()")
print(browser.window_handles)#获取当前所有窗口的句柄的列表
browser.switch_to.window(browser.window_handles[1])#切换选项卡
browser.get("https://www.taobo.com")
time.sleep(2)
browser.switch_to.window(browser.window_handles[0])
time.sleep(2)
browser.close()
time.sleep(2)
browser.switch_to.window(browser.window_handles[0])
browser.close()
反屏蔽
大多数的网页的检测原理,通过浏览器的窗口下的对象window.navigtor是否包含webdriver这个属性,在正常使用(非机器人)webdriver的属性值为undfined,然而我们使用selenium工具时就会给webdriver设置属性值。
反屏蔽方法:
需要执一段js代码
Object,defineProperty(nvigator, "webdriver", {
get: ()=> undefined})
但是不能使用browser.excute_script()方法去执行,应为这个方法时网页在加载完成之后执行的。我们需要的是刚开带来浏览器时就执行这个配置,去除我们selenium在浏览器窗口webdriver中设置的值。这个时候就会用到Chrome的开发工具
示例如下:
from selenium import webdriver
#基于web窗口的webdriver的属性来检测
url = "https://antispider1.scrape.cuiqingcai.com/"
option = webdriver.ChromeOptions()#chrome浏览器工具对象
option.add_experimental_option(
'excludeSwitches',
['enable-automation']
)
option.add_experimental_option("useAutomationExtension", False)
browser = webdriver.Chrome(options=option)
browser.execute_cdp_cmd("Page.addScriptToEvaluateOnNewDocument",
{
'source':'Object.defineProperty(navigator, "webdriver",{get: () => undefined})'})
browser.get(url)
import time
time.sleep(5)
browser.close()
无头模式
不实际的打开浏览器页面
from selenium import webdriver
from selenium.webdriver import ChromeOptions
option = ChromeOptions()
option.add_argument("--headless")
browser = webdriver.Chrome(options=option)
browser.set_window_size(1920,1080)
browser.get("https://www.baidu.com/")
browser.get_screenshot_as_file("baidu.png")
browser.close()
练习:
排行榜:先获取lis列表,然后利用循环将列表内容取出
详情页面:各个信息利用节点查找进行获取
推荐的爬取:需要等待页面中的详情加载完然后进行抓取
异步爬虫的原理和实战
在了解异步协程之前,先了解阻塞、非阻塞、同步、异步
阻塞:某个程序在计算时没有得到要使用的资源时被挂起。也就说程序在执行一个任务时,自身无法去处理其他的事务。
非阻塞:程序在执行过程中,自身不被阻塞,可以继续出处理其他的操作。非阻塞并不是任何程序级别、任何情况下都可以存在。只有程序封装级别的才可以非阻塞
同步:不同的程序单元为了完成某个任务,在执行过程以通讯的方式协调一致。然后这些程序单元就是同步的。
异步:为了某个任务,不同的程序不需要通信协调,也可以完成任务目标。这个时候就说这几个程序时异步的。
多进程:
多个进程利用CPU多核的优势并行的执行任务。可以大提高程序效率
协程:
协程,又叫微线程,纤程,轻量级的线程
协程拥有自己的寄存器上下文和栈。协程在调度切换时,将寄存器上下文和栈中的数据迁出保存到一个地方,等它再切回时再将数据迁回。本质上他还是一个单线程,比起多进程来说它没有线程的上文切换的开销,也没有元子操作锁定以及同步开销。
协程的使用
python中自3.5一个有asynci以它为基础作协程
概念:
示例
import asyncio
async def execute(x):
print(x)
coroutine = execute(1)
print("构造协程对象")
loop = asyncio.get_event_loop()#循环事件对象
task = loop.create_task(coroutine)#注册任务
loop.run_until_complete(task)#将协程对象注册到事件循环中并将其执行
print("end")
绑定回调
import requests
import asyncio
async def request():
url = "https://www.baidu.com/"
status = requests.get(url)
return status.status_code
def callback(task):
print("status:",task.result())
coroutine = request()
task = asyncio.ensure_future(coroutine)
task.add_done_callback(callback)
print("task:",task)
loop = asyncio.get_event_loop()
loop.run_until_complete(task)
print("task:",task)
多任务的协程
import requests
import asyncio
async def request():
url = "https://www.baidu.com/"
status = requests.get(url)
return status.status_code
def callback(task):
print("status:",task.result())
coroutine = request()
task = asyncio.ensure_future(coroutine)
task.add_done_callback(callback)
print("task:",task)
loop = asyncio.get_event_loop()
loop.run_until_complete(task)
print("task:",task)
协程的实现
awiat 不能直接接收response的返回,但是awiat后面可以跟一个coroutine(协程)对象,也是说我们用async把请求的方法换成coroutine对象不就行了
使用aiohttp
是一个支持异步请求的库,利用asyncio和它配合达到异步操作
安装方法
pip install -i https://pypi.douban.com/simple aiohttp
它分为两部分,一部分是server,一部分是client
在执行10个协程的时候,如果遇到await方法就会把当前的协程挂起,转去执行其他的协程,直到其他协程挂起或者执行完毕,然后再执行下一个任务。
协程是10 request函数,遇到await get(),直到其他协程挂起或者执行完毕,转入执行get,遇到await response.text(),直到其他协程挂起或者执行完毕,await session.close(),直到其他协程挂起或者执行完毕。最后退出函数
import asyncio
import requests
import time
import aiohttp
start_time = time.time()
async def get(url):
session = aiohttp.ClientSession()#构建aiohttp的client对象
response = await session.get(url)
await response.text()
await session.close()
return response
async def request():
url = "http://books.toscrape.com/catalogue/page-2.html"
print(f"waiting for {url}")
response = await get(url)
print(f"get response for {url}")
tasks = [asyncio.ensure_future(request()) for _ in range(10)]
loop = asyncio.get_event_loop()
loop.run_until_complete(asyncio.wait(tasks))
end_time = time.time()
print("cost time:",end_time - start_time)
示例:
http://books.toscrape.com/catalogue/page-1.html
思路:
构建任务列表
会把结果存放到一个列中
[[一页的所有的书的url],[],[]]
总结:
异步需要aiohttp和asyncio配合使用,然后利用async修饰函数。利用awiat挂起等待,请求利用aiohttp的session发起异步请求。将认任务注册到event_loop中进行循环执行
- 代理实际就指代理服务器,在本机和目标直接建立一个桥,从此本机不再直接向目标发起请求,由代理服务器发起之后将结果再返回给本机。
- 代理的作用:
- 突破自身的iP访问限制。
- 访问单位或者团体内部资源。
- 提高访问速度。一般代理服务器,会有很大的硬盘缓存区
- 隐藏真实IP
- 代理分类:
- FTP代理服务器
- HTTP代理
- SSL、TLS代理一般访问加密网站进行使用的代理
- RTSP主要式访问流媒体使用的代理服务器
- Telnet代理,远程链接代理。预防黑客攻击本机
- SOCKETs代理 单纯数据传输代理
- 高匿名
- 普通匿名
- 透明匿名。你访问时,它会把自己的ip和本机的IP都上报给服务器
代理设置
requests的设置方法讲过了
selenium的设置方法如示例
from selenium import webdriver
proxy = "127.0.0.1:8080"
options = webdriver.ChromeOptions()
options.add_argument("--proxy-server=http://"+proxy)
browser = webdriver.Chrome(options=options)
browser.get("http://httpbin.org/get")
print(browser.page_source)
browser.close()
aiohttp设置代理
import asyncio
import aiohttp
proxy = "http://127.0.0.1:8080"
async def main():
async with aiohttp.ClientSession() as session:
async with session.get("http://httpbin.org/get",proxy=proxy) as response:
print(await response.text())
if __name__ == '__main__':
loop = asyncio.get_event_loop()
loop.run_until_complete(main())
验证码:
用来区别用户时计算机还是人的公共全自动程序
验证码的作用:
验证码的反爬原理
利用地方平台做验证码验证
示例
#登录简书
import requests
from hashlib import md5
import time
from io import BytesIO
from PIL import Image
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver import ActionChains
#简书的账号密码
EMAIL = "15383467476"
PASSWORD = "lxs1995104."
#超级鹰的用户名,密码,软件ID,验证码类型
CHAOJIYING_USERNAME = "yuanpangzi"
CHAOJIYING_PASSWORD = "lxs1995104."
CHAOJIYING_SOFT_ID = "f70c3a48cbbe166d95a475ef21029d83"
CHAOJIYING_KIND = 9102
#超级鹰的第三接口类
class Chaojiying_Client(object):
def __init__(self, username, password, soft_id):
self.username = username
password = password.encode('utf8')
self.password = md5(password).hexdigest()
self.soft_id = soft_id
self.base_params = {
'user': self.username,
'pass2': self.password,
'softid': self.soft_id,
}
self.headers = {
'Connection': 'Keep-Alive',
'User-Agent': 'Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0)',
}
def PostPic(self, im, codetype):
"""
im: 图片字节
codetype: 题目类型 参考 http://www.chaojiying.com/price.html
"""
params = {
'codetype': codetype,
}
params.update(self.base_params)
files = {
'userfile': ('ccc.jpg', im)}
r = requests.post('http://upload.chaojiying.net/Upload/Processing.php', data=params, files=files, headers=self.headers)
return r.json()
def ReportError(self, im_id):
"""
im_id:报错题目的图片ID
"""
params = {
'id': im_id,
}
params.update(self.base_params)
r = requests.post('http://upload.chaojiying.net/Upload/ReportError.php', data=params, headers=self.headers)
return r.json()
#简书验证类
class CracToucClick():
def __init__(self):
self.url = "https://www.jianshu.com/sign_in"
self.browser = webdriver.Chrome()
self.wait = WebDriverWait(self.browser, 20)
self.email = EMAIL
self.password = PASSWORD
self.chaojiying = Chaojiying_Client(
CHAOJIYING_USERNAME,
CHAOJIYING_PASSWORD,
CHAOJIYING_SOFT_ID
)
def __del__(self):
self.browser.close()
def open(self):
'''
打开网页输出用户名密码
:return:None
'''
self.browser.get(self.url)#打开网页
#定位账号输入点和密码输入点
email = self.wait.until(EC.presence_of_element_located((By.ID, "session_email_or_mobile_number")))
password = self.wait.until(EC.presence_of_element_located((By.ID, "session_password")))
#填写内容
email.send_keys(self.email)
password.send_keys(self.password)
def get_touclick_button(self):
'''
获取初始验证按钮,加载验证码图片
:return:按键
'''
button = self.wait.until(EC.presence_of_element_located((By.CLASS_NAME, "sign-in-button")))
return button
def get_touclick_element(self):
'''
获取验证图片对象
:return:图片对象
'''
element = self.wait.until(EC.presence_of_element_located((By.CLASS_NAME, "geetest_widget")))
return element
def get_position(self):
'''
获取验证码位置
:return: 位置的元组
'''
element = self.get_touclick_element()
time.sleep(2)
location = element.location
size = element.size
print(size,location)
top,bottom,left,right = location['y']+100, 100+location['y']+size['height'], location['x'],location['x']+size["width"]
top = int(top)
bottom = int(bottom)
left = int(left)
right = int(right)
return (top, bottom, left, right)
def get_screenshot(self):
'''
网页截图
:return:截图对象
'''
screenshot = self.browser.get_screenshot_as_png()
screenshot = Image.open(BytesIO(screenshot))
return screenshot
def get_touclick_image(self, name="captch.png"):
'''
获取图片
:param name:
:return:图片对象
'''
top, bottom, left, right = self.get_position()
print("验证码的位置",top, bottom, left, right)
#截图对象
screenshot = self.get_screenshot()
captcha = screenshot.crop((top, bottom, left, right))
captcha.save(name)
return captcha
def get_points(self,captcha_result):
'''
解析识别结果
:param captcha_result:
:return:
'''
groups = captcha_result.get("pic_str").split("|")
locations = [[int(number) for number in group.split(",")] for group in groups]
print("locations:", locations)
return locations
def touch_click_words(self, locations):
'''
点击
:param locations:
:return:
'''
for location in locations:
print(location)
ActionChains(self.browser).move_to_element_with_offset(
self.get_touclick_element(), location[0],
location[1]
).click().perform()
time.sleep(1)
def touch_click_verify(self):
'''
点击验证码
:return:
'''
boutton = self.wait.until(EC.presence_of_element_located((By.CLASS_NAME, "geetest_commit")))
boutton.click()
def login(self):
'''
登录
:return:
'''
submit = self.wait.until(EC.presence_of_element_located((By.ID, "_submit")))
submit.click()
time.sleep(10)
print("登录成功")
def crack(self):
'''
入口函数
:return:
'''
self.open()#打开页面
#获取登录按钮
button = self.get_touclick_button()
button.click()
time.sleep(1)
#开始获取验证码图片
image = self.get_touclick_image()
# bytes_array = BytesIO()
# image.save(bytes_array, format="PNG")
# #验证码识别
# result = self.chaojiying.PostPic(bytes_array.getvalue(), CHAOJIYING_KIND)
# print(result)
# locations = self.get_points(result)
# self.touch_click_words(locations)
# self.touch_click_verify()
# #判断是否登录成功
# try:
# success = self.wait.until(
# EC.presence_of_element_located((By.CLASS_NAME, "btn write-btn"),"验证成功")
# )
# print(success)
# except:
# #失败进行重新验证
# self.crack()
if __name__ == '__main__':
crack = CracToucClick()
crack.crack()
用户输入信息点击登录之后,浏览器就会向服务器发送一个登录请求,这个请求当中携带了用户的信息,服务器就会进行验证,验证之后会给客户端也就是浏览器一个凭证,有了凭证之后,客户端就可以去访问页面的的其他内容。
session和cookies
在服务器中session记录了用户的状态。
cookies中只保存看了session ID的相关信息。服务器可以根据这个session id找到对应的session信息。这里面就有一个字段就记录了登录状态。
当然在cookies中也肯就直接存放了一些凭证信息。
JWT
近几年前后端分离越来越明显了,传统的前后端验证方式就是session和cookies.不是很适合前后端分离,后来就产生了JWT,(json web token),为了在网络运用传递和声明执行一种基于json的开放标准,实际上就是每次登录的时候同通过传递一个token字符串来记录登录状态。
JWT声明一般用来做身份提供者和服务提供者之间进行被认证的用户信息的传递,以便于客户端从服务器获取资源。也就是说,Token既可以被用来做身份验证,还可以进行信息的传递。
token是一个加密过的字符串。一般会以’.'号将toKen分成三段,
分别是header,payload,signature
header声明了JWT的签名算法,RSA、SHA256。也可能会包含JWT的编号,数据类型整个信息的base64编码。
payload 通常用来存放一些业务需要的但是又不是很敏感的信息。
signature这就是一个签名,是把header和pyloadd的信息用密钥加密后形成的,是保存在服务端的。
这3部分组合起来就是用户凭证了。
session和cookies的核心思想:就是你登录前后使用session和cookies要保持是一对儿。
jwt,就是你要以正确的方式请求是添加上这个jwt的信息就可以以登录状态访问。
对于网页来说,执行逻辑都是依靠JS实现的,JS的代码是运行在客户端的,也就说js代码要在客户端加载运行,它也就是变成了透明的。因此导致JS的代码是不安全的,任何人都可以进行读、分析、复制、盗用和修改
就需要JS的压缩、混淆、加密技术
代码压缩:去除代码中不必要的空格、换行等内容,减低代码的可读性。还可以加代码的执行速度
代码混淆:使用变量替换、字符串阵列化、控制流平坦化、多态变异、僵尸函数、调试保护等手段。就是让你找不到变量,就是让你看不懂。就是找不到逻辑。
代码加密:可以通过一些手段对代码进行加密,转换成人无法理解阅读的代码。但是计算机可以执行的。抽象化加密,eval加密。。
接口加密技术
js混淆:
JS逆向分析思路
分析URL构成以及请求方式和数据加载方式
根据url中关键字搜索js代码文件
根据url进行XHR断点调试
寻找callstack中执行过的内容找到加密的字段
将api/moviefan放到一个列表中
列中加入时间戳
将列表内容用逗号拼接
将拼接好的结果用SHA1进行编码
再将编码的结果和时间戳进行逗号拼接
将拼接的结果以base64进行编码
分析完之后转换为python
import time
import base64
from typing import List,Any
import requests
import hashlib
index_url = "https://dynamic6.scrape.cuiqingcai.com/api/movie?limit={limit}&offset={offset}&token={token}"
limit = 10
offset = 0
def get_token(args:List[Any]):
timestamp = str(int(time.time()))
args.append(timestamp)
sign = hashlib.sha1(",".join(args).encode("utf-8")).hexdigest()
token_ = base64.b64encode(",".join([sign, timestamp]).encode("utf-8")).decode("utf-8")
return token_
args = ["/api/movie"]
token = get_token(args=args)
print(token)
url = index_url.format(limit=limit,offset=offset,token=token)
response = requests.get(url)
print(response.json())
- 基于node.js开发的一个工具,有了它就可以通过js控制你的浏览器,也可以用在爬虫是,API相当完善。就是类似与selenium,selenium的功能它全有。它其实Puppeteer的python实现版本叫Pyppeteer,在Pyppeteer中它实际上类似与Chrome执行一些动作,Chorome再发布正式的版本之前会先在Chromium上测试执行。
- 安装
pip install -i https://pypi.douban.com/simple pyppeteer
比如我们用requests.aiohttp去爬取数据的时候,异常处理。数据存储,任务调度等,从头写到尾你的的代码还不一定健全
所以为了提高爬虫的编写效率就有了框架。
简介:
安装
项目结构
基本运用:
目标:创建项目、创建一个spider来抓取站点数据,然后进行处理,通过命令将抓的内容导出,将抓取的数据入库mongodb
创建spider的方法:
创建item
解析response
加入iltem
后续的url的请求发起
运行保存到文件中
使用Item pipeline
将结果保存到mongo中,或者进行筛选有用的item的就可以定义item pipLine来进行实现
常用的操作
实现也很简单就是在item pipline中定义一个process_item方法。启用这个方法之后就可以调用,需要注意的是你必须在返回结果中包含数据的字典或者iltem对象,或者抛出异常DropIltem异常
process_item有两参数一个item每次产生的item都会作为参数传递过来,另一个就是spider它是Spider对象。
open_spider 必须包含一个参数spider
close_spider 必须包含一个参数spider
在setting.py中开启item_pipline的功能
Spider
Spider类
除了以上基础属性还有一些常用方法:
Selector
这个选择器时基于lxml构建的,支持xpth选择器和css选择器
示例
from scrapy import Selector
body = """
hello world
"""
selector = Selector(text=body)
title = selector.xpath("//title/text()")
print(title)
print(title.extract_first())
scrapy shell
功能强大到不行的中间件
Spider Middlewares
Downloader Middlewares
process_requests(request, spider)
process_response(requests, response, spider):
process_exception(reqeust,exception,spider)
如果遇到了Ajax页面
动态渲染的页面请求
分布式爬虫
去重:
防止中断
实现
原理
实例
scrapy部署
容器:
docker,爬虫可直接做成容器进行运行。在构建容器前需要修改与外部有连接的地址信息。先导出爬虫使用到库的信息,然后进入容器,容器启动前进行安装。
scrapy对接kubernetes
总结:从小白到高手必经之路爬虫
数据有web端,也有移动端(手机上面的APP).
APP的数据就比较依赖独立的服务器,比如请求一个登录接口来获取验证,其实类似与web中的ajax,手机端给服务器发起了请求,请求格式有很多种,json,XML,基本不会用HTML这种格式的。
为了了解app在运行过程中如何发起请求,然后拿到数据的。就会使用抓包工具。Fiddler,Charles,mitmproxy,anyproxy。
使用了工具还有可能拿不到数据。就是APP本身设置了不走代理。APP设置了SSL.还有带了加密参数。为了破解加密你就要做逆向分析。APP为了防止逆向还做了逆向加固,就是成为一个壳。将核心代码进行编译形成一个so库,因此就是要对so库进行逆向才能够了解器逻辑。服务器还做风控处理。
抓包
手机代理:
配置方法:
工具需要修改自己的手机配置,具体方法都和手机的相关
脱壳:
脱壳工具:FRIDA-DEXDump,Dumpdex
反汇编:
模拟:
adb
触动精灵,按键精灵
Appium
AirTest
Appium/Airtest + mitmdump
mitproxy: