数据获取途径
注意:robots.txt
1、浏览器版Chrome
2、手机版Chrome
3、合作网站(猪队友网站)、子网站
请求方式
requests.get(url,headers = headers,verify=False,proxies = proxies);
requests.post(url,data=data,headers = headers,verify=False,proxies = proxies)
Requests: (第三方库)
Response = requests.get(url,headers = headers)
Response = requests.post(url,data=data,headers = headers)
Response: (requests生成的实例对象)
Response.text 获取str 数据
Response.content 获取byte 数据 可以使用.decode()解码
Response.status_code 获取返回状态码(状态码200不一定代表数据获取成功,仅代表请求成功)
Response.request.headers 获取请求头内容。
Response.headers 查看请求的数据类型,(可以看到是json格式,utf-8编码)
Proxies: 使用代理
Response = requests.get(url,headers = headers,proxies = proxies)
Prioxies形式:字典
Proxies = {
“http”:”http://123.123.123”,
“http”:”http://456.123.555”.
“http”:”http://123.977.123”.
................
}
处理cookies、session请求:
使用方法:
1、实例化一个session对象
2、让session发送get或者post请求
Session =requests.session()
Response = session.get(url,headers)
如果需要携带cookie访问网站,需要获取cookie。
三种方式:
1、需要登录,借助session进行第一次访问将cookie写入。获取cookie
2、其他途径获取大量cookie,直接将cookie写入请求data={}字典中
3、Get请求获取cookie并写入请求data中。
反爬机制
通常防止爬虫被反主要有以下几个策略:
1、动态设置User-Agent(随机切换User-Agent,模拟不同用户的浏览器信息)
2、禁用Cookies(也就是不启用cookies middleware,不向Server发送cookies,有些网站通过cookie的使用发现爬虫行为)
3、可以通过COOKIES_ENABLED 控制 CookiesMiddleware 开启或关闭
4、Google Cache 和 Baidu Cache:如果可能的话,使用谷歌/百度等搜索引擎服务器页面缓存获取页面数据。
5、使用IP地址池:VPN和代理IP,现在大部分网站都是根据IP来ban的。
6、使用 Crawlera(专用于爬虫的代理组件),正确配置和设置下载中间件后,项目所有的request都是通过crawlera发出。
7、设置延迟下载(防止访问过于频繁,设置为 2秒 或更高)
DOWNLOADER_MIDDLEWARES = {
'scrapy_crawlera.CrawleraMiddleware': 600
}
CRAWLERA_ENABLED = True
CRAWLERA_USER = '注册/购买的UserKey'
CRAWLERA_PASS = '注册/购买的Password'
使用代理前,先检查代理ip可不可用。
使用requests模块检查ip可用不可用,如果ip地址存放在redis中。使用程序不挺读取ip地址,发起请求。判断ip地址是否可用。如果不可用直接删掉。在使用scrapy获取Ip时,大部分都是可用的。
如果购买代理,需要使用到账号密码,使用bas64加密。参考html课件。
Url
建议:
如果url请求路径有很多,无法确定真实的请求url路径,可以尝试提交内容的改变,查看请求路径中不变的Url. 参考可能需要的参数是否包含在From date中。增加判断真确的几率。
1、from表单中的action=”www.xxx.com”可以获得
Key、value是充from表单中的input标签中获取。
<input name=”email”>
<input name=”password”>
2、通过浏览器中Network中抓包
1. 通过输入错误密码,获得请求路径
2. 通过Chrome中勾选Preserve log功能,防止页面跳转找不到url,获取请求路径Name信息。
备注:
通过pyquery的history属性,发现baidu这些连接大多进行了redirect(http 302),但是有一部分链接是直接获得了http200回复。对于从百度爬取的加密的url,进行requests.get()时不允许跳转(allow_redirects=False)。然后针对这两类服务器回复分别处理:
http 302跳转:从headers中的'location'可以获得原始url;
http 200回复:从content中通过正则表达式获取原始url
3、火狐浏览器中编辑请求头:
网络 → 消息头 → 可以编辑请求头(增加或删除referer等请求头)重发。
数据来源的获取
一 、如果获取的数据是写死的,不改变的。可以直接拿来用,不需要了解作用,
二 、如果获取的数据是动态变换的,需要研究其生成方式,比如js方法
三 、如果传递相同的数据,得到的数据每次都是不同的,可以判定使用了【加盐】,需要了解生成方式,使用Python代码完成相同逻辑。加密的两种方式:服务器保存key、value,将盐值发送浏览器,逻辑加密后返回key、value;第二种相反。
1、通过Chremo中的Search all files进行全局数据搜索。
2、代码中打断点,实现代码的逻辑数据显示。
3、定位想要的js:
3.1 触发事件:通过Chremo选择会触发的js事件的【按钮】,Elements中Event Listeners ,Framework listeners功能锁定js代码。
3.2 隐藏的传输事件:通过Chremo中Network中需要数据的Request URl 地址,通过路径中的需要的参数(url关键字),寻找js代码。全局搜索Search all files。
4、在Sourses来源中查看相应JS代码。
5、将未知的功能粘贴至Console中,实现反转。
相关软件加载(淘宝NPM镜像):http://npm.taobao.org
Url路径拼接
url路径解码:
XPATH 重点
一 、 在chrome浏览器中的Elements中查看html进行调试,但是爬虫获取的是url对应的响应,往往不同于elements中的格式。(应从实际已请求的返回值进行筛选,因为使用etree工具可能解析有误,导致节点锁定错误)需要从Network或者程序中使用etree.tostring打印修改后返回的html数据格式。
二 、 可以使用chremo中的xpath helper插件提供的Copy→Copy XPath功能,直接生成选中节点的路径。
获取文本
a/text() 获取a下的文本
a//text() 获取a下的所有标签的文本
//a[text()='下一页'] 选择文本为下一页三个字的a标签
@符号: 选取属性
a/@href
//ul[@id="detail-list"]
/ 下一级(层)
// 下任何级(层)
在xpath最前面表示从当前html中任意位置开始选择
li//a 表示的是li下任何一个标签
li[2] li标签第二个
li[last()-3] li标签倒数第三个
ll[position()<4] li标签前三个
| 或
//book/title | //book/price
1 、Contains 包含
//div[contains(@class, ”i” )] 表示所有div中包含class=”i”的标签
2 、Not 不包含
特殊用法:
/book[price>35.00]/title 选取book元素中,price元素的值大于35.00的title元素
节点选择法:
* 匹配任何元素节点
@* 匹配任何属性节点
node() 匹配任何类型的节点
lxml能够接受bytes和str的字符串
提取页面数据的思路:
先分组,取到一个包含分组标签的列表 (先取对象,生成对象列表)
遍历,取其中每一组进行数据的的提取,不会造成数据的对应错乱。 (遍历对象列表,针对每一层对象单独写获取方式。配合三元表达式完成逻辑判断)
Xpath的更多语法:https://msdn.microsoft.com/zh-cn/library/ms256039(v=vs.80).aspx
Queue队列
进程会等所有的线程结束后再结束
设置守护线程,把当前的线程作为一个从属的线程,依赖于另外一个线程,另外一个线程结束,当前线程就结束。
1、Queue.emty 判断当前队列是都是空的,如果是回复True
2、Queue.full 判断是都满
3、Queue.get 从池中取数据
4、Queue.get_nowait 取数据,取不到就报错
5、Queue.task_done() 对任务进行计数
6、Queue.put 推数据到池子中
7、Queue.put_nowait 推数据到池子,不成功报错
8、Queue.qsize 获取队列保存个数。
9、Queue.join 让主线程堵塞(让主线程等待队列的任务数),等待子线程结束后主线程结束(python3中)
10、Thread.setDaemon(Ture) 让该线程变成守护线程(该线程不重要,主线程结束,该线程结束)
11、
12、
注意:Queue.put 计数+1 ,Queue.get和Queue.task_done一起使用 计数-1
动态请求HTML(优化请求)
1 、 尽量减少请求次数(减少反扒几率)
能抓列表页,不抓详情页。
保存获取到的html页面,供插错和重复请求使用
2 、 关注网站的所有类型的页面(爬取难度:极速版页面,手机版页面,电脑版页面)
Wap页面,触屏版页面
H5页面
APP
3 、 多伪装(防止被反爬)
动态UA(headers={“User-Agent”:“http://.....”,。。。。}})
代理ip
不使用cookie
4 、 利用多线程分布式
在不被ban的请求下尽可能的提高速度
Selenium 和 PhantomJS
Selenium是 Web的自动化测试工具。 (效率比较低,访问页面需要加载完才能进行数据的获取,速度慢。) 可以直接运行在浏览器上,支持所有主流浏览器(包括PhantomJS无边界浏览器),可以接收命令,让浏览器自动加载页面,获取需要的数据,甚至页面截屏。
driver = webdriver.Chrome() # 实例化一个浏览器 (Chrome需要安装)
driver.get(‘http://www.baidu.com’) # 发送请求
time.sleep(3) # 睡3秒
driver.quit() # 退出浏览器
driver.get_cookies() # 获取cookies 列表
列表推导式: cookies = {i["name"]:i["value"] for i in cookies_list}
driver.page_source # 浏览器中elements的内容,浏览器所有内容加载完毕后的html字符串
driver.current_url # 页面加载/跳转完成后的url地址。
driver.close() # 退出当前页面
元素定位的方法:
find_element_by_id (根据id返回一个)
find_elements_by_xpath (返回列表,如果路径写错,获取不到返回空列表 [] )
find_elements_by_link_text (根据连接的文本选择)
find_elements_by_partial_link_text (根据连接的部分文本选择)
find_elements_by_tag_name (根据标签选择)
find_elements_by_class_name (根据class选择)
find_elements_by_css_selector (根据CSS选择)
获取文本和获取属性
先定位到元素,然后调用.text或者get_attribute方法来去
selenium获取的页面数据是浏览器中elements的内容
findelement和findelements的区别
find_element返回一个element,如果没有会报错
find_elements返回一个列表,没有就是空列表
在判断是否有下一页的时候,使用find_elements来根据结果的列表长度来判断
如果页面中含有iframe、frame,需要先调用driver.switch_to.frame的方法切换到frame中才能定位元素
<iframe id = “aaa” name = “bbb” ..............>
driver.switch_to.frame(“aaa”)
或driver.switch_to.frame(“bbb”)
或driver.switch_to.frame(Selenium选择的元素)
细节重点:
selenium请求第一页的时候回等待页面加载完了之后在获取数据,但是在点击翻页之后,hi直接获取数据,此时可能会报错,因为数据还没有加载出来,需要time.sleep(3)
selenium中find_elements_by_class_name智能接收一个class对应的一个值,不能传入多个
driver.find_element_by_id(“kw”).send_key(“python”) # 选中input框,输入内容“python”
driver.find_element_by_id(“su”).click() # 选中input框,执行点击事件。
PhantomJS 是 无界面(headless)浏览器 , 它将网站加载到内存并执行页面JavaScript。
driver = webdriver.PhantomJS() # 实例化内存浏览器 (PhantomJS需要安装)
driver.set_window_size() # 设置截屏大小 如(1980,1080)
driver.maximize_window() # 最大化窗口
driver.save_screenshot(“./name.png”) 保存截屏图片位置、类型
Cookie相关用法
{cookie[‘name’]:cookie[‘value’] for cookie in driver.get_cookies()} 列表推导式
driver.delete_cookie(“CookieName”) 删除cookie
driver.delete_all_cookies()
应用场景:
1、cookie过期时间很长,常见于一些不规范的网站
2、能在cookie过期之前把搜有的数据拿到
3、配合其他程序使用,比如其使用selenium把登陆之后的cookie获取到保存到本地,scrapy发送请求之前先读取本地cookie
注意:
1、在scrapy框架中,setting中默认cookis在下一次请求中默认携带的。如果第一次请求中获得,或者携带cookie后面的请求就会携带从传递。
2、Scrapy中携带cookies请求不能写到headers中。原生的requests模块可以将cookies写到headers中。
验证码
1、验证码的识别
url不变,验证码不变
2、请求验证码的地址,获得相应,识别
url不变,验证码会变
使用Selenium工具。
使用截图工具PIL 模块,可以实现截图。注意,需要将浏览器宽高固定,要不然截图位置无法定位。
思路:对方服务器返回验证码的时候,会和每个用户的信息和验证码进行一个对应,之后,在用户发送post请求的时候,会对比post请求中法的验证码和当前用户真正的存储在服务器端的验证码是否相同
验证码的获取需要当前用户的cookie,所以需要session发验证码请求,写入session中一个cookie;
如果使用requests发送请求,需要携带cookie值,并将cookie值在不同的请求间传递使用。
1. 实例化session
2.使用seesion请求登录页面,获取验证码的地址
3.使用session请求验证码,识别
4.使用session发送post请求’
使用selenium登录,遇到验证码
url不变,验证码不变,同上
url不变,验证码会变
1.selenium请求登录页面,同时拿到验证码的地址
2.获取登录页面中driver中的cookie,交给requests模块发送验证码的请求,识别
3.输入验证码,点击登录
云打码平台:http://www.yundama.com/l
账号:183************
密码:******
下载相关运行包:
# 用户名
username = '183********'
# 密码
password = '**********'
# appid
appid = 4283
# appkey
appkey = '02074c64f0d0bb9efb2df455537b01c3'
# 验证码类型
# 验证码题分价格:http://www.yundama.com/price.html 写codetype前先看下攻关网站的验证码是那种类型的,对比响应类型填写。
codetype = 3007
# 超时
timeout = 60
注意点:
1、获取html中的标签路径时,elements中的<tbody>标签并不存在。
2、可以使用 切片 [1:-1]来过滤获取的list。
3、From表单提交中,完整内容应该包还有:
用户名、密码
csrf_token
action method post enctype=application/www-x-urlencoded
authenticity_token
数据的格式整理
1、通过Chremo浏览器中Sources中的{} :Pretty print功能实现格式的转换
2、Chremo中安装JSONView插件。
3、网页在线
4、Pychem
5、电脑安装Atom
1、json.dumps ; json.loads ; json.dump ; json.load ; str() ; eavl() 用法区别:
json.dumps()
Python数据类型 → json字符串
将dict类型的数据转成str
json.loads()
json字符串 → Python数据类型
json.loads()用于将str类型的数据转成dict。
注:具有read()或者write()方法的对象就是类文件对象
f=open(“a.txt”,”r”) f就是类文件对象
json.dump()
python数据类型 → 包含json的类文件对象
json.dump()用于将dict类型的数据转成str,并写入到json文件中。
json.load()
包含json的类文件对象 → python数据类型
json.load()用于从json文件中读取数据。
Str()
虽然可以转换为字符串,但是在write写入过程中,没有json.dumps灵活,例如:f.write(json.dumps(data,ensure_ascii=False,indent=2))可以携带格式参数。
Eavl()
处理不了层级太多的代码,容易出错。
2、lxml库
lxml可以自动修正/补全html代码(如果传递的数据标签有缺失/不规范,可能导致修正后格式有误。)
利用etree.HTML 将字符串(str/byte类型)转化为Element对象
Element对象具有xpath的方法
from lxml import etree
html = etree.HTML(text) # todo 返回的是html对象
etree.tostring( html ) # todo 获取element对象中包含的byte类型的字符串
html.xpath(“语句”) # todo 获取的是个列表集合(根据获取路径可以是:对象、herf属性/text文本)
3、Print( etree.tostring(html对象).decode() )
查看通过etree修正后的html格式。
Scrapy shell交互终端
我们可以在未启动spider的情况下尝试及调试代码,也可以用来测试XPath表达式
使用方法:
1、进入ipython/python交互终端
2、scrapy shell http://www.itcast.cn/channel/teacher.shtml
response.url:当前响应的url地址
response.request.url:当前响应对应的请求的url地址
response.headers:响应头
response.body:响应体,也就是html代码,默认是byte类型
response.requests.headers:当前响应的请求头
注意:response.headers会采用默认的请求头,所以需要先设置User-Agent:
3、自动拼接不完整的url路径:urljoin()
import urllib
a = “aaa/aaa/aa/aaa/aa/aaa/ccc/ccc/cc/c/cccc”
b = “bbb/bbbb/bbbb/bbbb/bbb”
urllib.parse.urljoin(a,b)
→”aaaa/aaa/aaa/aaa/bbb/bbb/bbb/bbb/bbb/bbb”
数据的清洗
1、Response =requests.get(“http://www.baidu.com”)
Get请求获取响应
2、response.cookies
返回值中可以提取cookies
3、requests.utils.dict_from_cookiejar(response.cookies)
Cookie转字典
4、Requests.utils.cookiejar_from_dict({“key”:”value”})
字典转换cookie
5、url地址解码:
Requests.utils.unquote(“http%2f%2f..............”)
→“http://...............”
6、url地址编码:
Repuest.utils.quote(“http://........”)
→”%ad123%232%sdfds.........”
7、SSLError:
SSL证书问题,可能存在安全隐患。(Https)
解决方法:添加verify=False response = requests.get(“http://www.baidu.com”, verify = False)
8、设置请求时间限制参数
timeout = 10 response = requests.get(url , timeout = 10)
9、Eval
eval() 能实现简单的字符串和Python类型的转换
10、Replace
把单引号替换为双引号
11、re.compile(编译)
Compile将字符串按照传入的正则方法,预先匹配一遍,当调用compile对象的方法时,提高效率。
注意:如果使用compile() , re.S需要放到compile(“.” , re.S)中
例如:
b = “asdasd123”
p = re.compile(“\d”)
p.findall(b)
→ [“1”,”2”,”3”]
12、match(从头找一个)
13、findall(找所有)
Re.findall(“.” , “\n”, re.DOTALL)
在DOTALL模式中可以匹配换行符。re.DOTALL也可以写成re.S
14、sub(替换)
re.sub(“\d”,”_”,b) 从b中匹配第一个条件”\d”,为”_”。
15、递归函数:
16、列表推导式
content = [re.sub(r"\xa0|\s","",i) for i in content]
content = [i for i in content if len(i)>0] #去除列表中的空字符串
17、[ 0 : -1]
18、注意数据的覆盖。Item结构(item的深拷贝)
Item如果设置的传递层数太多,再写入数据库中,会因为底层的函数字段相同而覆盖。所以传递item中,在适当节点需要使用硬拷贝deepcopy()函数。deepcopy(item)进行参数对象的传递,保证数据不会覆盖。
19、随机数random用法:
random.random()函数是这个模块中最常用的方法了,它会生成一个随机的浮点数,范围是在0.0~1.0之间。
random.uniform()正好弥补了上面函数的不足,它可以设定浮点数的范围,一个是上限,一个是下限。
random.randint()随机生一个整数int类型,可以指定这个整数的范围,同样有上限和下限值,python random.randint。
random.choice()可以从任何序列,比如list列表中,选取一个随机的元素返回,可以用于字符串、列表、元组等。
random.shuffle()如果你想将一个序列中的元素,随机打乱的话可以用这个函数方法。
random.sample()可以从指定的序列中,随机的截取指定长度的片断,不作原地修改。
20、Url编码/解码:
当url地址含有中文或者“/”的时候,这是就需要用urlencode做以下编码转换:
urlencode的参数是词典,它可以将key-value这样的键值对转换成我们想要的格式。
如果你用的是python2.*,urlencode在urllib.urlencode。
如果使用的是python3,urlencode在urllib.parse.urlencode
当urlencode之后的字符串传递过来之后,接受完毕就要解码了——urldecode.urllib提供了unquote()这个函数
Requests.utils.unquote(aaa)
21、<noscript>在禁用js时,里面的内容才会显示。
22、选择式数据提取,排除空数据方式。
item["m_cate"] = dl.xpath("./dt//text()").extract()
item["m_cate"] = [i.strip() for i in item["m_cate"] if len(i.strip())>0][0]
23、去除空白字符
string.lstrip() 截掉 string 左边(开始)的空白字符
string.rstrip() 截掉 string 右边(末尾)的空白字符
string.strip() 截掉 string 左右两边的空白字符
数据导出
Mongoexport
数据的存储
直接写成文本:
With open(“name.txt”,”w”,encoding=”utf-8”) as f:
f.write(json.dumps(data,ensure_ascii=False,indent=2))
encoding=”utf-8” 设置打开方式
ensure_ascii=False 消除默认ascii码。
indent=2 整理代码格式,设置换行缩进2个字符。
数据库
Mongodb
非关系型数据库
查看数据库 show dbs / show databases
查看当前数据库 db
使用(创建)数据库 use test (只有插入数据,创建的数据库才会显示)
删除当前数据库 db.dropDatabase()
向不存在的集合中第一次加入数据,集合会被创建 db.createCollection(name,options)
给集合设置上限:db.createCollection(“sub”,{capped:true,size:10}) true 表示设置上限
查看集合:show collections
删除集合:db.集合名称.drop()
Object ID :文档ID
String : 字符串,最常用,必须是有效的UTF-8
Boolean :存储一个布尔值,true或fales
Integer : 整数可以是32位或64位,这取决于服务器
Double : 存储浮点值
Arrays : 数组或列表,多个值存储到一个键
Object : 用于嵌入式的文档,即一个值为一个文档
Null : 存储Null 值
Timestamp: 时间戳,表示从1970-1-1到现在的总秒数
Date: 存储当前日期或时间的UNIX时间格式
创建时间 : new Date(‘2017-12-20’)
插入一条数据: db.nametest.insert({“name”:”xiaowang”,”age”:”18”})
objectID是一个12字节的16进制数:
前4个字节是时间戳
接下来3个字节的机器ID
接下来的2个字节中MongoDB的服务进程id
最后3个字节是简单的增量值
更改数据: db.集合名称.save(document)
如果文档的_id已经存在则修改,如果文档的_id不存在则添加
查询 db.集合名称.find()
更新 db.集合名称.update(<query>,<update>,{multi:<boolean>})
参数query: 查询条件
参数update: 更新操作符
参数multi: 可选,默认是false, 表示只更新找到到第一条记录
值为true 表示把满足条件的文档全部更新
替换一整条:db.stu.update({name:’hr’},{name:’mnc’})
更新对应项一条:db.stu.update({name:’hr’},{$set:{name:’hys’}})
更新全部:db.stu.update({},{$set:{gender:0}},{multi:true})
注意:”multi update only works with $ operators”
删除:db.集合名称.remove(<query>,{justOne:<boolean>})
参数query:可选,删除的文档的条件
参数justOne:可选,如果设为true或1,则只删除一条,默认false,表示删除多条
查询:find() db.集合名称.find({条件文档})
查询一个:findOne() db.集合名称.findOne({条件文档})
将结果格式化:方法pretty(): db.集合名称.find({条件文件}).pretty()
比较运算符
小于:$lt (less than)
小于等于:$lte (less than equal)
大于:$gt (greater than)
大于等于:$gte
不等于:$ne
例: db.stu.find({age:{$gte:18}})
范围: “$in” , “nin”
例: db.stu.find({age:{$in:[18:28:38]}})
逻辑运算符
And:
db.stu.find({age:{j$gte:18},gender:true})
Or:
Db.stu.find({$or:[{age:{}$gt:18}],{gender:false}})
Db.stu.find({$or:{$gt:18}},{gender:true}],name:’guojing’)
$in $nin
Db.stu.find({age:{$in:[18,28,38]}})
Re正则
Db.products.find({sku:/^abc/})
Db.products.find({sku:{regex:’789$’}})
Limit和skip
读取指定数量文档: db.集合名称.find().limit(NUMBER)
查询2条学生信息
db.stu.find().limit(2)
跳过指定数量的文档: db.集合名称.find().skip(NUMBER)
Db.stu.find().skip(2)
Db.stu.find().limit(4).skip(5)
自定义查询:
使用$where后面写一个函数:
Db.stu.find({$where:function(){return this.age>30;}})
投影:在查询的结果中,只选择必要的字段
Db.集合名称.find({},{字段名称:1},...)
参数为字段与值,值为1表示显示,值为0不显示
特殊:对于_id列默认是显示,如果不显示需要明确设置为0
Db.stu.find({},{_id:0,name:1,gender:1})
升序降序 1 / -1
Db.stu.find().sort({age:1})
Db.stu.find().sort({age:-1})
Db.stu.find().sort({age:-1,gender:-1})
查询所有只有字段名字:
Db.stu.find({},{name:1,_id:0})
统计个数:
Db.集合名称.find({条件}).count()
Db.集合名称.count({条件}).count()
Db.stu.find({gender:true}).count()
Db.stu.count({age:{$gt:20},gender:true})
消除重复
Db.集合名称.distinct(‘去重字段’,{条件})
Db.stu.distinct(‘hometown’,{age:{$gt:18}})
聚合 aggregate
Aggregate基于数据处理的聚合管道,每个文档通过一个由多个阶段(stage)组成的管道,可以对每个阶段的管道进行分组、过滤等功能,然后经过一系列的处理,输出相应的结果。
Db.集合名称.aggregate({管道:{表达式}})
常用的管道:
$group: 将集合中的文档分组,可用于统计结果
$match: 过滤数据,只输出符合条件的文档
$project: 修改输入文档的结构,如重命名、增加、删除字段、创建计算结果
$sort: 将输入文档排序后输出
$limit: 限制聚合管道返回的文档数
$skip: 跳过指定数量的文档,并返回余下的文档
$unwind: 将数组类型的字段进行拆分
表达式:
$sum: 计算总和, $sum:1 表示以一倍计数
$avg: 计算平均值
$min: 获取最小值
$max: 获取最大值
$push: 在结束文档中插入值到一个数组中
$first: 根据资源文档的排序获取第一个文档数据
$last: 根据资源文档的排序获取最后一个文档数据
实例化pytmong
from pymongo import MongoClient
#实例化client,建立连接 。如果是本机(内容可以不写)
client = MongoClient(host="127.0.0.1",port=27017)
collection = client["test"]["t251"]
#插入一条数据 _id 自动随机生成 (insert接收字典,返回objectID)
# ret1 = collection.insert({"name":"xiaowang","age":10})
# collection.insert( item )
#插入多条数据
# data_list = [{"name":"test{}".format(i)} for i in range(10)]
# collection.insert_many(data_list)
#查询一个记录
# t = collection.find_one({"name":"xiaowang"})
#查询所有记录
t = collection.find({"name":"xiaowang"})
print(t) # t 是游标集合对象,取值只能遍历一次
List(t) # (强制类型转换为列表)
# insert_many接收一个列表,列表中为所有要插入的字典
t = self.collection.insert_many(item_list)
# t.inserted_ids为所有插入的id
for i in t.inserted_ids:
Print(i)
# find_one查找并且返回一个结果,接收一个字典形式的条件
# find 返回所有满足条件的结果,如果条件为空,则返回数据的所有
# update_one更新一条数据
# update_many 跟新全部数据
# delete_one 删除一条数据
# delete_many 删除所有满足条件的数据
终端命令
数据备份:
mongodump -h dbhost -d dbname -o dbdirectory
-h : 服务器地址
-d : 需要备份的数据库名称
-o : 备份的数据存放位置
mongodump -h 192.168.196.128:27017 -d test1 -o ~/Desktop/test1bak
数据恢复:
mongorestore -h dbhost -d dbname -o dbdirectory
-h : 服务器地址
-d : 需要恢复的数据库名称
--dir : 备份的数据所在位置
例:mongorestore -h 192.168.196.128:27017 -d newdb--dir ~/Desktop/test1bak/olddb
添加索引
在默认情况下创建的索引均不是唯一索引。
创建唯一索引:
db.t1.ensureIndex({"name":1},{"unique":true})
创建唯一索引并消除重复:
db.t1.ensureIndex({"name":1},{"unique":true,"dropDups":true})
建立联合索引(什么时候需要联合索引):
db.t1.ensureIndex({name:1,age:1})
查看当前集合的所有索引:
db.t1.getIndexes()
删除索引:
db.t1.dropIndex('索引名称')
Redis
中文文档: http://www.redis.cn/commands.html
Redis-cli 进入redis
Select 0 选择数据库0
Keys * 查看当前数据库
Type 数据名 : 产看当前数据类型 zset 有序类型,set无序类型
Fluchdb清空当前db
1、clone github scrapy-redis源码文件
git clone https://github.com/rolando/scrapy-redis.git
2、研究项目自带的三个demo
mv scrapy-redis/example-project ~/scrapyredis-project
Scrapy_redis路径过滤介绍:
将request对象生成指纹,并不会带上headers进行生成,因为请求过程中,cookie会不同,如果带上headers一起生成指纹,就会出现,相同的路径生成的指纹不同。
Scrapy_redis如何生成指纹的?
F = Hashlib.sha1()
Celery
Celery是一个任务队列,同时支持任务调度。
Backend: 后台,如redis
Broker:相当于消息中间件
三方扩展包
Tesseract 光学识别库
图像翻译成文字的COR库.识别率不稳定,图片对比度越明显真确率越高。
安装:
sudo apt-get install tesseract-ocr
pip install pytesseract
在终端中调用Tesseract
tesseract abc.jpg test
Python中使用pytesseract识别图片,需要使用PIL模块中的Image读取后的数据:
Import pytesseract
from PIL import.open(jpg)
image = Image.open(jpg)
pytesseract.image_to_string(image)
scrapy框架
Scrapy 使用了 Twisted['twɪstɪd]异步网络框架,可以加快我们的下载速度。
创建一个scrapy项目
scrapy startproject mySpider
生成一个爬虫
cd mySpider
scrapy genspider itcast "itcast.cn”
提取数据
完善spider,使用xpath等方法
保存数据
pipeline中保存数据
运行:
Scrapy crawl itcast
逻辑、方法
注意:
1 、 scrapy框架中提供的extract_first()方法,获取不到值会显示None,可以省去if判断逻辑。
2 、 在使用yield item 给pielines.py传参时,需要将settings.py中的ITEM_PIPELINES(注释打开)设置执行pipeline顺序,框架才会采用yield传参,并按顺序执行。
3 、 yield 传递的参数必须是:Request; BaseItem; dict; None四中类型。
4 、 需要爬取的url地址必须属于allowed_domains下的连接,超出范围,需要添加新的范围到allowed_domains中。
5 、 response.xpath() 返回的是一个含有selector对象的列表。
6 、 item保存到mongodb中时,需要使用dict(item)进行转换。
7 、 allowed_domains =[‘abdc.com’]如果范围设置不正确,导致没有结果。
选择器提取字符串:
1、extract() 返回一个包含有在字符串数据的列表
2、extract_first() 返回列表中的一个字符串
方法:
Spiders中的每一个(爬虫.py)中的类属性name=”xxx”,对应pipelines.py中一个process_item(self,item,sipder)中的sipder.name。
items.py中可以定义多个TencentItem类,pipelines.py中使用isinstance(item,TencentItem)判断使用的是哪个item。
Item.Field()是个字典。
可以通过logging模块中的logging.warning(xxxx)将内容写到日志中。(debug;info;)
使用logger = logging.getLogger(__name__)方法可以将报错的当前Py文件显示出来,方便差错。Logger.warning(xxxx)
Logging.basicConfig(设置log日志保存样式,百度搜编写格式)
scrapy.Request(next_page_url,callback = self.parse)构造一个requsets请求,传入url,设置执行函数。如果还行函数类型相同,可以使用同样个parse,否则自己设置一个请求函数。
构造一个请求Request(url,dont_fileter=True)如果不指定callback。默认会将请求经过parse函数处理。
通过重写Spider中的def start_requests(self):方法,可以实现自定义方式的关于start_urls的请求。比如添加headers、cookies等。
Scrapy GET请求
scrapy.Request知识点:
scrapy.Request(url, [ callback,method=’GET’,headers,body,cookies,meta,dont_filter=False ] )注:[]中的参数表示可选。
Meta : 传参(item)时需要和请求一起传递到下一个函数中调用。
Dont_filter=False :表示默认过滤url,相同的url只请求一次。
官方文档:http://scrapy-chs.readthedocs.io/zh_CN/1.0/intro/overview.html
scrapy模拟POST请求(登陆之发送)
使用scrapy发送Post请求的3中方式:
1、scrapy.FormRequest(url,formdata ={},callback=’’)
2、Scraoy.Request(url,method=”POST”,dody=’’)
3、Scrapy.FormRequest.from_response(response,formdata={},callbak=) # 自动从response中寻找form表单。
Scrapy.FormRequest知识点:
Scrapy.FormRequest.from_response()
注意:只能单独的表单提交界面,并且页面中的form表单中可以直接找到action="/login/../.."属性,不能是js生成的。才能使用from_response方法请求。
使用formdata携带需要的post数据
Yield
可以通过添加不同的数据字段,实现函数调用或数据提取前的判断(通过加if判断在pipilines.py中使用不同的函数调用,或者在代码中使用不同的yield函数。)。
item['type'] = "读书"
yield scrapy.Request(
item["s_href"],
callback=self.parse_cate_content,
meta={"item": deepcopy(item)},
)
yield item
想要在pipelines.py中获得item,就需要在最后调用的函数中回传item。
Pipelines.py 管道
import something
class SomethingPipeline(object):
def __init__(self):
# 可选实现,做参数初始化等
# doing something
def process_item(self, item, spider):
# item (Item 对象) – 被爬取的item
# spider (Spider 对象) – 爬取该item的spider
# 这个方法必须实现,每个item pipeline组件都需要调用该方法,
# 这个方法必须返回一个 Item 对象,被丢弃的item将不会被之后的pipeline组件所处理。
return item
def open_spider(self, spider):
# spider (Spider 对象) – 被开启的spider
# 可选实现,当spider被开启时,这个方法被调用。
def close_spider(self, spider):
# spider (Spider 对象) – 被关闭的spider
# 可选实现,当spider被关闭时,这个方法被调用
Setting.py中设置
ITEM_PIPELINES = {
'Something.pipelines.SomethingPipeline: 300,
......
......
}
Middlewares.py 中间件
下载中间件是处于引擎(crawler.engine)和下载器(crawler.engine.download())之间的一层组件,可以有多个下载中间件被加载运行。
当引擎传递请求给下载器的过程中,下载中间件可以对请求进行处理 (例如增加http header信息,增加proxy信息等);
在下载器完成http请求,传递响应给引擎的过程中, 下载中间件可以对响应进行处理(例如进行gzip的解压等)
DOWNLOADER_MIDDLEWARES = {
'mySpider.middlewares.MyDownloaderMiddleware': 543,
}
Setting设置
# -*- coding: utf-8 -*-
# Scrapy settings for yangguang project
#
# For simplicity, this file contains only settings considered important or
# commonly used. You can find more settings consulting the documentation:
#
# https://doc.scrapy.org/en/latest/topics/settings.html
# https://doc.scrapy.org/en/latest/topics/downloader-middleware.html
# https://doc.scrapy.org/en/latest/topics/spider-middleware.html
# redis增量爬虫设置
# 指定那个去重方法给request对象去重
# 调度器 指定scheduler队列
# 让redis持续保存数据 设置False,会在关闭redis的时候清空redis
# 设置路径操作redis
DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter"
SCHEDULER = "scrapy_redis.scheduler.Scheduler"
SCHEDULER_PERSIST = True
REDIS_URL = "redis://127.0.0.1:6379"
# scrapy_redis实现的items保存到redis的pipeline。
ITEM_PIPELINES = {
......
'scrapy_redis.pipelines.RedisPipeline': 400,
}
# 也可以写成
# REDIS_HOST = “192.168.207.124”
# REDIS_PORT = 6379
-----------------------------------------------------------------------------------------------
# 项目名
BOT_NAME = 'yangguang'
# 爬虫位置
SPIDER_MODULES = ['yangguang.spiders']
# 新建爬虫位置
NEWSPIDER_MODULE = 'yangguang.spiders'
# 查看cookies传递路径
COOKIES_DEBUG =True
# 设置报警级别
LOG_LEVEL="WARNING"
# 设置log日志保存的地址(终端中不再显示)
# LOG_FILE="./log.log"
# 设置请求中的 User-Agent(浏览器的身份标识,用户代理)
#USER_AGENT = 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3278.0 Safari/537.36'
# 遵守robots协议.(网站中允许爬去的范围)
ROBOTSTXT_OBEY = True
# 设置mongo存储位置为本机
MONGO_HOST="local_host"
# 配置Scrapy执行的最大并发请求(默认值:16)
#CONCURRENT_REQUESTS = 32
#配置对同一网站要求延迟(默认值:0秒)
#DOWNLOAD_DELAY = 3
# 每个域名请求并发数
#CONCURRENT_REQUESTS_PER_DOMAIN = 16
# 每个ip请求并发数
#CONCURRENT_REQUESTS_PER_IP = 16
# 禁用cookie(默认启用)
#COOKIES_ENABLED = False
# 禁用Telnet控制台(默认启用)
#TELNETCONSOLE_ENABLED = False
# 覆盖默认请求头 (注:User-Agent不能写到这里)
#DEFAULT_REQUEST_HEADERS = {
# 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
# 'Accept-Language': 'en',
#}
# 启用或禁用spider中间件
# See https://doc.scrapy.org/en/latest/topics/spider-middleware.html
#SPIDER_MIDDLEWARES = {
# 'yangguang.middlewares.YangguangSpiderMiddleware': 543,
#}
# 启用或禁用downloader中间件
# See https://doc.scrapy.org/en/latest/topics/downloader-middleware.html
#DOWNLOADER_MIDDLEWARES = {
# 'yangguang.middlewares.YangguangDownloaderMiddleware': 543,
#}
# 启用或禁用扩展
# See https://doc.scrapy.org/en/latest/topics/extensions.html
#EXTENSIONS = {
# 'scrapy.extensions.telnet.TelnetConsole': None,
#}
#配置项目管道
# See https://doc.scrapy.org/en/latest/topics/item-pipeline.html
# 开启ITEM_PIPELINES,yield 才能接收item返回到pipelines.py中调用,存入mongodb数据库。 (300是权重值,越小越先执行)
ITEM_PIPELINES = {
'yangguang.pipelines.YangguangPipeline': 300,
}
# 启用并配置自动节流阀扩展(默认禁用) 防止请求过快,将服务器抓崩。
# See https://doc.scrapy.org/en/latest/topics/autothrottle.html
#AUTOTHROTTLE_ENABLED = True
# The initial download delay
#AUTOTHROTTLE_START_DELAY = 5
# The maximum download delay to be set in case of high latencies
#AUTOTHROTTLE_MAX_DELAY = 60
# The average number of requests Scrapy should be sending in parallel to
# each remote server
#AUTOTHROTTLE_TARGET_CONCURRENCY = 1.0
# Enable showing throttling stats for every response received:
#AUTOTHROTTLE_DEBUG = False
# 启用和配置HTTP缓存(默认禁用)
# See https://doc.scrapy.org/en/latest/topics/downloader-middleware.html#httpcache-middleware-settings
#HTTPCACHE_ENABLED = True
#HTTPCACHE_EXPIRATION_SECS = 0
#HTTPCACHE_DIR = 'httpcache'
#HTTPCACHE_IGNORE_HTTP_CODES = []
#HTTPCACHE_STORAGE = 'scrapy.extensions.httpcache.FilesystemCacheStorage'
Scrapy
(scrapy.Spider) :普通爬虫,再次启动会从新爬去数据,start_url地址请求重复,造成数据冗余。
scrapy genspider itcast "itcast.cn”
(CrawlSpider) :自动提取url地址
生成crawlspider的命令:
Scrapy genspider -t crawl csdn “csdn.cn”
Rule(LinkExtractor(allow=r'可以是不完整的url'), callback='parse_item',follow=True),
# LinkExtractor 连接提取器,提取url地址
# callback 提取出来的url地址的response会交给callback处理
# follow 当前url地址的响应是够重新进过rules来提取url地址
# allow中写正则
Rule(LinkExtractor(allow=r'Items/'), callback='parse_item', follow=True),
Parse函数有特殊功能,不能定义。
Scrapy_redis
Scrapy_redis在scrapy的基础上实现了更多,更强大的功能,具体体现在:reqeust去重,爬虫持久化,和轻松实现分布式
“””spider that reads urls from redis queue (myspider:start_urls)”””
# 指定redis_key的读取(键)位置,程序会以Pop的读取方式,可以实现多台服务器同时完成一个任务,实现分布式爬虫。redis中存储的myspider:start_urls键所提供的(路径)数据。
redis_key = “myspider:start_urls”
(RedisSpider) :分布式爬虫,非重复,可间断,连续自动提取url地址,发送请求
爬虫的创建:
Scrapy startproject myspider
Scrapy genspider dangdang dangdang.com
from scrapy_redis.spiders import RedisSpider
class DangdangSpider(RedisSpider):
name = 'dangdang'
allowed_domains = ['dangdang.com']
# start_urls = ['http://book.dangdang.com/']
redis_key = "dangdang"
def parse(self, response):
终端运行:
Scrapy crawl dangdang
Redis中添加第一个请求地址:
redis-cli
lpush dangdang http://book.dangdang.com/
(RedisCrawlSpider) :自动提取url地址,实现分布式爬虫
from scrapy_redis.spiders import RedisCrawlSpider
class MyCrawler(RedisCrawlSpider):
"""Spider that reads urls from redis queue (myspider:start_urls)."""
name = 'mycrawler_redis'
redis_key = 'spider'
allowed_domain = []
rules = (
# restrict_xpaths不需要精确匹配指定的url地址,
Rule(LinkExtractor(restrict_xpaths=('//div[@class="...."]')或allow=r'Items/'), callback='parse_item', follow=True),
)
# def __init__(self, *args, **kwargs):
# # Dynamically define the allowed domains list.
# domain = kwargs.pop('domain', '')
# self.allowed_domains = filter(None, domain.split(','))
# super(MyCrawler, self).__init__(*args, **kwargs)
def parse_page(self, response):
return {
'name': response.css('title::text').extract_first(),
'url': response.url,
}
LinkExtractor常见参数
# -*- coding: utf-8 -*-
import scrapy
from scrapy.linkextractors import LinkExtractor
from scrapy.spiders import CrawlSpider, Rule
from scrapy_redis.spiders import RedisCrawlSpider
import re
class AmazonSpider(RedisCrawlSpider):
name = 'amazon'
allowed_domains = ['amazon.cn']
# start_urls = ['https://www.amazon.cn/%E5%9B%BE%E4%B9%A6/b/ref=sd_allcat_books_l1?ie=UTF8&node=658390051']
redis_key = "amazon"
rules = (
#匹配大分类的url地址和小分类的url
Rule(LinkExtractor(restrict_xpaths=("//div[@class='categoryRefinementsSection']/ul/li",)), follow=True),
#匹配图书的url地址
Rule(LinkExtractor(restrict_xpaths=("//div[@id='mainResults']/ul/li//h2/..",)),callback="parse_book_detail"),
#列表页翻页
Rule(LinkExtractor(restrict_xpaths=("//div[@id='pagn']",)),follow=True),
)
def parse_book_detail(self,response):
# with open(response.url.split("/")[-1]+".html","w",encoding="utf-8") as f:
# f.write(response.body.decode())
item = {}
item["book_title"] = response.xpath("//span[@id='productTitle']/text()").extract_first()
item["book_publish_date"] = response.xpath("//h1[@id='title']/span[last()]/text()").extract_first()
item["book_author"] = response.xpath("//div[@id='byline']/span/a/text()").extract()
# item["book_img"] = response.xpath("//div[@id='img-canvas']/img/@src").extract_first()
item["book_price"] = response.xpath("//div[@id='soldByThirdParty']/span[2]/text()").extract_first()
item["book_cate"] = response.xpath("//div[@id='wayfinding-breadcrumbs_feature_div']/ul/li[not(@class)]/span/a/text()").extract()
item["book_cate"] = [i.strip() for i in item["book_cate"]]
item["book_url"] = response.url
item["book_press"] = response.xpath("//b[text()='出版社:']/../text()").extract_first()
# item["book_desc"] = re.findall(r'<noscript>.*?<div>(.*?)</div>.*?</noscript>',response.body.decode(),re.S)
# item["book_desc"] = response.xpath("//noscript/div/text()").extract()
# item["book_desc"] = [i.strip() for i in item["book_desc"] if len(i.strip())>0 and i!='海报:']
# item["book_desc"] = item["book_desc"][0].split("<br>",1)[0] if len(item["book_desc"])>0 else None
print(item)
报文
1 、Overridden settings: settings.py中设置的内容。
2 、 Dumping Scrapy stats: 爬取数据的统计内容。
3 、关于正则中出现的错误:
TypeError: int() argument must be a string, a bytes-like object or a number, not '_sre.SRE_Match'
解决:缺少.group()方法提取。
re.search(r” ”,data).group(1)
4 、 LinkExtractor :连接提取器
5 、 TelnetConsole 错误
因为程序手动关闭后,并没有真正的结束,再次启动会提示内存中暂用有程序。并不影响后续代码的执行和数据的获取。不用修改。
Crontab软件:爬虫定时执行
安装:apt-get install cron(服务器环境下默认安装有)
使用:crontab -e 进入编辑页面(第一次会让你选择编辑器)
Crontab -l 查看当前定时任务
常用数据单词
URL编码:%3a%2f%.....
Location 地址
扩展
安装第三方模块:
1 、Pip install 包名
2 、压缩包文件中有setup.py文件,可以直接使用python setup.py 进行安装
3 、 ***.whl 文件 安装方法:pip install ***.whl
第三方模块:
1、requests
Import requests 导入模块
2、lxml
from lxml import etree 导入etree功能
文本格式:
XML :
被设计用来传输和存储数据。(同JSON一样用来传输数据)
HTML :
被设计用来显示数据。
CSV :
CSV是一种通用的、相对简单的文件格式。广泛的应用是在程序之间转移表格数据。Excel表格工具可以直接打开。
PIL 模块,可以实现截图。注意,需要将浏览器宽高固定,要不然截图位置无法定位。
Iframe 前端HTML中的一种框架,可以在一个html页面中嵌套另一个iframe页面。
PhantomJS:无边界浏览器(selenium中加载到内存)
了解方法:显式等待/隐式等待 (爬虫day06-07)
Hash 哈希
又称:散列、字典
linux(ubuntu)???phantomjs <http://www.cnblogs.com/lgh344902118/p/6369054.html>
phantomjs各个版本的下载路径
<https://bitbucket.org/ariya/phantomjs/downloads/>
安装方法:
1.9.8那个版本的phantomjs在Python3中也可以用
<https://www.cnblogs.com/lgh344902118/p/6369054.html>
列表排序
Lambda表达式:↓
终端命令:
Ctrl + Z 并没有将当前程序结束,而是隐藏到后台。
通过:ps aux | grep scrapy 可以查看对应文件运行情况
杀死程序: kill 编码
必杀: Kill 9
Pycharm中调bug
工具
Chremo中安装JSONView插件。
网页在线解码
网页在线格式转换
Pychem
Atom
总结
### xpath的包含
- `//div[contains(@class,'i')]`
实现爬虫的套路
- 准备url
- 准备start_url
- url地址规律不明显,总数不确定
- 通过代码提取下一页的url
- xpath
- 寻找url地址,部分参数在当前的响应中(比如,当前页码数和总的页码数在当前的响应中)
- 准备url_list
- 页码总数明确
- url地址规律明显
- 发送请求,获取响应
- 添加随机的User-Agent,反反爬虫
- 添加随机的代理ip,反反爬虫
- 在对方判断出我们是爬虫之后,应该添加更多的headers字段,包括cookie
- cookie的处理可以使用session来解决
- 准备一堆能用的cookie,组成cookie池
- 如果不登录
- 准备刚开始能够成功请求对方网站的cookie,即接收对方网站设置在response的cookie
- 下一次请求的时候,使用之前的列表中的cookie来请求
- 如果登录
- 准备多个账号
- 使用程序获取每个账号的cookie
- 之后请求登录之后才能访问的网站随机的选择cookie
- 提取数据
- 确定数据的位置
- 如果数据在当前的url地址中
- 提取的是列表页的数据
- 直接请求列表页的url地址,不用进入详情页
- 提取的是详情页的数据
- 1. 确定url
- 2. 发送请求
- 3. 提取数据
- 4. 返回
- 如果数据不在当前的url地址中
- 在其他的响应中,寻找数据的位置
- 1. 从network中从上往下找
- 2. 使用chrome中的过滤条件,选择出了js,css,img之外的按钮
- 3. 使用chrome的search all file,搜索数字和英文
- 数据的提取
- xpath,从html中提取整块的数据,先分组,之后每一组再提取
- re,提取max_time,price,html中的json字符串
- json
- 保存
- 保存在本地,text,json,csv
- 保存在数据库