爬虫质量分析,Python之爬虫总括

一、爬虫之requests

Python之爬虫总括,python爬虫

一、爬虫之requests

   
a、介绍:应用requests能够萧规曹随浏览器的央求,比起从前使用的urllib,requests模块的api尤其方便人民群众(本质正是包装了urllib3)

   
b、注意:requests发送请求是将网页内容下载来以后,并不会进行js代码,那亟需我们友好分析目的站点然后发起新的requests请求

    c、安装:pip3 install requests

    d、各样请求格局,常用的是requests.get()和requets.post()

二、基于get请求

    a、基本请求

import requests
response=requests.get('http://dig.chouti.com/')
print(response.text)

    b、带参数get请求—–》》params

    c、带参数get请求—–》》headers

#通常我们在发送请求时都需要带上请求头,请求头是将自身伪装成浏览器的关键,常见的有用的请求头如下
Host
Referer #大型网站通常都会根据该参数判断请求的来源
User-Agent #客户端
Cookie #Cookie信息虽然包含在请求头里,但requests模块有单独的参数来处理他,headers={}内就不要放它了

     d、带参数get请求—–》》cookies

三、基于post请求

     a、介绍

#GET请求
HTTP默认的请求方法就是GET
     * 没有请求体
     * 数据必须在1K之内!
     * GET请求数据会暴露在浏览器的地址栏中

GET请求常用的操作:
       1. 在浏览器的地址栏中直接给出URL,那么就一定是GET请求
       2. 点击页面上的超链接也一定是GET请求
       3. 提交表单时,表单默认使用GET请求,但可以设置为POST


#POST请求
(1). 数据不会出现在地址栏中
(2). 数据的大小没有上限
(3). 有请求体
(4). 请求体中如果存在中文,会使用URL编码!


#!!!requests.post()用法与requests.get()完全一致,特殊的是requests.post()有一个data参数,用来存放请求体数据
复制代码

爬虫质量分析,Python之爬虫总括。    b、发送POST的央浼,模拟浏览器的记名行为

四、响应Response

    a、response属性

澳门葡京备用网址 1

import requests
respone=requests.get('http://www.jianshu.com')
# respone属性
print(respone.text)
print(respone.content)

print(respone.status_code)
print(respone.headers)
print(respone.cookies)
print(respone.cookies.get_dict())
print(respone.cookies.items())

print(respone.url)
print(respone.history)

print(respone.encoding)

#关闭:response.close()
from contextlib import closing
with closing(requests.get('xxx',stream=True)) as response:
    for line in response.iter_content():
    pass

View Code

    b、编码难题

#编码问题
import requests
response=requests.get('http://www.autohome.com/news')
# response.encoding='gbk' #汽车之家网站返回的页面内容为gb2312编码的,而requests的默认编码为ISO-8859-1,如果不设置成gbk则中文乱码
print(response.text)

    c、获取二进制

#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)

    d、解析json

#解析json
import requests
response=requests.get('http://httpbin.org/get')

import json
res1=json.loads(response.text) #太麻烦

res2=response.json() #直接获取json数据


print(res1 == res2) #True

五、selenium模块

    a、介绍

selenium最初是一个自动化测试工具,而爬虫中使用它主要是为了解决requests无法直接执行JavaScript代码的问题

selenium本质是通过驱动浏览器,完全模拟浏览器的操作,比如跳转、输入、点击、下拉等,来拿到网页渲染之后的结果,可支持多种浏览器

from selenium import webdriver
browser=webdriver.Chrome()
browser=webdriver.Firefox()
browser=webdriver.PhantomJS()
browser=webdriver.Safari()
browser=webdriver.Edge()

   b、安装

#安装:selenium+chromedriver
pip3 install selenium
下载chromdriver.exe放到python安装路径的scripts目录中即可,注意最新版本是2.29,并非2.9
国内镜像网站地址:http://npm.taobao.org/mirrors/chromedriver/2.29/
最新的版本去官网找:https://sites.google.com/a/chromium.org/chromedriver/downloads

#注意:
selenium3默认支持的webdriver是Firfox,而Firefox需要安装geckodriver
下载链接:https://github.com/mozilla/geckodriver/releases

六、选择器

   a、基本选拔

澳门葡京备用网址 2

from selenium import webdriver
from selenium.webdriver import ActionChains
from selenium.webdriver.common.by import By #按照什么方式查找,By.ID,By.CSS_SELECTOR
from selenium.webdriver.common.keys import Keys #键盘按键操作
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.wait import WebDriverWait #等待页面加载某些元素
import time

driver=webdriver.Chrome()
driver.get('https://www.baidu.com')
wait=WebDriverWait(driver,10)

try:
    #===============所有方法===================
    # 1、find_element_by_id
    # 2、find_element_by_link_text
    # 3、find_element_by_partial_link_text
    # 4、find_element_by_tag_name
    # 5、find_element_by_class_name
    # 6、find_element_by_name
    # 7、find_element_by_css_selector
    # 8、find_element_by_xpath
    # 强调:
    # 1、上述均可以改写成find_element(By.ID,'kw')的形式
    # 2、find_elements_by_xxx的形式是查找到多个元素,结果为列表

    #===============示范用法===================
    # 1、find_element_by_id
    print(driver.find_element_by_id('kw'))

    # 2、find_element_by_link_text
    # login=driver.find_element_by_link_text('登录')
    # login.click()

    # 3、find_element_by_partial_link_text
    login=driver.find_elements_by_partial_link_text('录')[0]
    login.click()

    # 4、find_element_by_tag_name
    print(driver.find_element_by_tag_name('a'))

    # 5、find_element_by_class_name
    button=wait.until(EC.element_to_be_clickable((By.CLASS_NAME,'tang-pass-footerBarULogin')))
    button.click()

    # 6、find_element_by_name
    input_user=wait.until(EC.presence_of_element_located((By.NAME,'userName')))
    input_pwd=wait.until(EC.presence_of_element_located((By.NAME,'password')))
    commit=wait.until(EC.element_to_be_clickable((By.ID,'TANGRAM__PSP_10__submit')))

    input_user.send_keys('18611453110')
    input_pwd.send_keys('[email protected]')
    commit.click()

    # 7、find_element_by_css_selector
    driver.find_element_by_css_selector('#kw')

    # 8、find_element_by_xpath

    time.sleep(5)

finally:
    driver.close()

View Code

   b、xpath

   c、获取标签属性

from selenium import webdriver
from selenium.webdriver import ActionChains
from selenium.webdriver.common.by import By #按照什么方式查找,By.ID,By.CSS_SELECTOR
from selenium.webdriver.common.keys import Keys #键盘按键操作
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.wait import WebDriverWait #等待页面加载某些元素

browser=webdriver.Chrome()

browser.get('https://www.amazon.cn/')

wait=WebDriverWait(browser,10)
wait.until(EC.presence_of_element_located((By.ID,'cc-lm-tcgShowImgContainer')))

tag=browser.find_element(By.CSS_SELECTOR,'#cc-lm-tcgShowImgContainer img')

#获取标签属性,
print(tag.get_attribute('src'))


#获取标签ID,位置,名称,大小(了解)
print(tag.id)
print(tag.location)
print(tag.tag_name)
print(tag.size)


browser.close()

七 、等待成分加载

     
a、selenium只是模仿浏览器的一言一行,而浏览器解析页面是索要时日的(执行css,js),一些因素恐怕必要过一段时间才能加载出来,为了保障能查找到成分,必须等待

      b、等待的艺术分两种:

               
隐式等待:在browser.get(’xxx’)前就安装,针对富有因素有效

       
 显式等待:在browser.get(’xxx’)之后设置,只针对有个别元素有效

八 、爬虫之解析库—-re,beautifulsoup、pyquery

      a、介绍

Beautiful Soup 是一个可以从HTML或XML文件中提取数据的Python库.它能够通过你喜欢的转换器实现惯用的文档导航,查找,
修改文档的方式.Beautiful Soup会帮你节省数小时甚至数天的工作时间.你可能在寻找 Beautiful Soup3 的文档,Beautiful Soup 3
 目前已经停止开发,官网推荐在现在的项目中使用Beautiful Soup 4, 移植到BS4

     b、安装

安装:Beautifulsoup4
      pip3 install beautifulsoup4
安装解释器:
      Beautiful Soup支持Python标准库中的HTML解析器,还支持一些第三方的解析器,其中一个是 lxml .根据操作系统不同,可以选择下列方法来安装lxml:

九 、遍历文书档案数

#遍历文档树:即直接通过标签名字选择,特点是选择速度快,但如果存在多个相同的标签则只返回第一个
#1、用法
#2、获取标签的名称
#3、获取标签的属性
#4、获取标签的内容
#5、嵌套选择
#6、子节点、子孙节点
#7、父节点、祖先节点
#8、兄弟节点

澳门葡京备用网址 3

#遍历文档树:即直接通过标签名字选择,特点是选择速度快,但如果存在多个相同的标签则只返回第一个
html_doc = """
<html><head><title>The Dormouse's story</title></head>
<body>
<p id="my p" class="title"><b id="bbb" class="boldest">The Dormouse's story</b></p>

<p class="story">Once upon a time there were three little sisters; and their names were
<a href="http://example.com/elsie" class="sister" id="link1">Elsie</a>,
<a href="http://example.com/lacie" class="sister" id="link2">Lacie</a> and
<a href="http://example.com/tillie" class="sister" id="link3">Tillie</a>;
and they lived at the bottom of a well.</p>

<p class="story">...</p>
"""

#1、用法
from bs4 import BeautifulSoup
soup=BeautifulSoup(html_doc,'lxml')
# soup=BeautifulSoup(open('a.html'),'lxml')

print(soup.p) #存在多个相同的标签则只返回第一个
print(soup.a) #存在多个相同的标签则只返回第一个

#2、获取标签的名称
print(soup.p.name)

#3、获取标签的属性
print(soup.p.attrs)

#4、获取标签的内容
print(soup.p.string) # p下的文本只有一个时,取到,否则为None
print(soup.p.strings) #拿到一个生成器对象, 取到p下所有的文本内容
print(soup.p.text) #取到p下所有的文本内容
for line in soup.stripped_strings: #去掉空白
    print(line)


'''
如果tag包含了多个子节点,tag就无法确定 .string 方法应该调用哪个子节点的内容, .string 的输出结果是 None,如果只有一个子节点那么就输出该子节点的文本,比如下面的这种结构,soup.p.string 返回为None,但soup.p.strings就可以找到所有文本
<p id='list-1'>
    哈哈哈哈
    <a class='sss'>

            <h1>aaaa</h1>

    </a>
    <b>bbbbb</b>
</p>
'''

#5、嵌套选择
print(soup.head.title.string)
print(soup.body.a.string)


#6、子节点、子孙节点
print(soup.p.contents) #p下所有子节点
print(soup.p.children) #得到一个迭代器,包含p下所有子节点

for i,child in enumerate(soup.p.children):
    print(i,child)

print(soup.p.descendants) #获取子孙节点,p下所有的标签都会选择出来
for i,child in enumerate(soup.p.descendants):
    print(i,child)

#7、父节点、祖先节点
print(soup.a.parent) #获取a标签的父节点
print(soup.a.parents) #找到a标签所有的祖先节点,父亲的父亲,父亲的父亲的父亲...


#8、兄弟节点
print('=====>')
print(soup.a.next_sibling) #下一个兄弟
print(soup.a.previous_sibling) #上一个兄弟

print(list(soup.a.next_siblings)) #下面的兄弟们=>生成器对象
print(soup.a.previous_siblings) #上面的兄弟们=>生成器对象

用法

View Code

10、搜索文书档案数

    a、三种过滤器

#1、五种过滤器: 字符串、正则表达式、列表、True、方法
#1.1、字符串:即标签名
print(soup.find_all('b'))

#1.2、正则表达式
import re
print(soup.find_all(re.compile('^b'))) #找出b开头的标签,结果有body和b标签

#1.3、列表:如果传入列表参数,Beautiful Soup会将与列表中任一元素匹配的内容返回.下面代码找到文档中所有<a>标签和<b>标签:
print(soup.find_all(['a','b']))

#1.4、True:可以匹配任何值,下面代码查找到所有的tag,但是不会返回字符串节点
print(soup.find_all(True))
for tag in soup.find_all(True):
    print(tag.name)

#1.5、方法:如果没有合适过滤器,那么还可以定义一个方法,方法只接受一个元素参数 ,如果这个方法返回 True 表示当前元素匹配并且被找到,如果不是则反回 False
def has_class_but_no_id(tag):
    return tag.has_attr('class') and not tag.has_attr('id')

     b、find_all

澳门葡京备用网址 4

#2、find_all( name , attrs , recursive , text , **kwargs )
#2.1、name: 搜索name参数的值可以使任一类型的 过滤器 ,字符窜,正则表达式,列表,方法或是 True .
print(soup.find_all(name=re.compile('^t')))

#2.2、keyword: key=value的形式,value可以是过滤器:字符串 , 正则表达式 , 列表, True .
print(soup.find_all(id=re.compile('my')))
print(soup.find_all(href=re.compile('lacie'),id=re.compile('\d'))) #注意类要用class_
print(soup.find_all(id=True)) #查找有id属性的标签

# 有些tag属性在搜索不能使用,比如HTML5中的 data-* 属性:
data_soup = BeautifulSoup('<div data-foo="value">foo!</div>','lxml')
# data_soup.find_all(data-foo="value") #报错:SyntaxError: keyword can't be an expression
# 但是可以通过 find_all() 方法的 attrs 参数定义一个字典参数来搜索包含特殊属性的tag:
print(data_soup.find_all(attrs={"data-foo": "value"}))
# [<div data-foo="value">foo!</div>]

#2.3、按照类名查找,注意关键字是class_,class_=value,value可以是五种选择器之一
print(soup.find_all('a',class_='sister')) #查找类为sister的a标签
print(soup.find_all('a',class_='sister ssss')) #查找类为sister和sss的a标签,顺序错误也匹配不成功
print(soup.find_all(class_=re.compile('^sis'))) #查找类为sister的所有标签

#2.4、attrs
print(soup.find_all('p',attrs={'class':'story'}))

#2.5、text: 值可以是:字符,列表,True,正则
print(soup.find_all(text='Elsie'))
print(soup.find_all('a',text='Elsie'))

#2.6、limit参数:如果文档树很大那么搜索会很慢.如果我们不需要全部结果,可以使用 limit 参数限制返回结果的数量.效果与SQL中的limit关键字类似,当搜索到的结果数量达到 limit 的限制时,就停止搜索返回结果
print(soup.find_all('a',limit=2))

#2.7、recursive:调用tag的 find_all() 方法时,Beautiful Soup会检索当前tag的所有子孙节点,如果只想搜索tag的直接子节点,可以使用参数 recursive=False .
print(soup.html.find_all('a'))
print(soup.html.find_all('a',recursive=False))

'''
像调用 find_all() 一样调用tag
find_all() 几乎是Beautiful Soup中最常用的搜索方法,所以我们定义了它的简写方法. BeautifulSoup 对象和 tag 对象可以被当作一个方法来使用,这个方法的执行结果与调用这个对象的 find_all() 方法相同,下面两行代码是等价的:
soup.find_all("a")
soup("a")
这两行代码也是等价的:
soup.title.find_all(text=True)
soup.title(text=True)
'''

find_all

     c、find

澳门葡京备用网址 5

#3、find( name , attrs , recursive , text , **kwargs )
find_all() 方法将返回文档中符合条件的所有tag,尽管有时候我们只想得到一个结果.比如文档中只有一个<body>标签,那么使用 find_all() 方法来查找<body>标签就不太合适, 使用 find_all 方法并设置 limit=1 参数不如直接使用 find() 方法.下面两行代码是等价的:

soup.find_all('title', limit=1)
# [<title>The Dormouse's story</title>]
soup.find('title')
# <title>The Dormouse's story</title>

唯一的区别是 find_all() 方法的返回结果是值包含一个元素的列表,而 find() 方法直接返回结果.
find_all() 方法没有找到目标是返回空列表, find() 方法找不到目标时,返回 None .
print(soup.find("nosuchtag"))
# None

soup.head.title 是 tag的名字 方法的简写.这个简写的原理就是多次调用当前tag的 find() 方法:

soup.head.title
# <title>The Dormouse's story</title>
soup.find("head").find("title")
# <title>The Dormouse's story</title>

View Code

     d、css选择器

#1、CSS选择器
print(soup.p.select('.sister'))
print(soup.select('.sister span'))

print(soup.select('#link1'))
print(soup.select('#link1 span'))

print(soup.select('#list-2 .element.xxx'))

print(soup.select('#list-2')[0].select('.element')) #可以一直select,但其实没必要,一条select就可以了

# 2、获取属性
print(soup.select('#list-2 h1')[0].attrs)

# 3、获取内容
print(soup.select('#list-2 h1')[0].get_text())

十一、爬虫之MongoDB

    a、简介

MongoDB是一款强大、灵活、且易于扩张的通用型数据库

    b、易用性

MongoDB是一个面向文档(document-oriented)的数据库,而不是关系型数据库。
不采用关系型主要是为了获得更好得扩展性。当然还有一些其他好处,与关系数据库相比,面向文档的数据库不再有“行“(row)的概念取而代之的是更为灵活的“文档”(document)模型。
通过在文档中嵌入文档和数组,面向文档的方法能够仅使用一条记录来表现复杂的层级关系,这与现代的面向对象语言的开发者对数据的看法一致。
另外,不再有预定义模式(predefined schema):文档的键(key)和值(value)不再是固定的类型和大小。由于没有固定的模式,
根据需要添加或删除字段变得更容易了。通常由于开发者能够进行快速迭代,所以开发进程得以加快。而且,实验更容易进行。开发者能尝试大量的数据模型,从中选一个最好的。

     c、易扩大性

应用程序数据集的大小正在以不可思议的速度增长。随着可用带宽的增长和存储器价格的下降,即使是一个小规模的应用程序,需要存储的数据量也可能大的惊人,甚至超出
了很多数据库的处理能力。过去非常罕见的T级数据,现在已经是司空见惯了。
由于需要存储的数据量不断增长,开发者面临一个问题:应该如何扩展数据库,分为纵向扩展和横向扩展,纵向扩展是最省力的做法,但缺点是大型机一般都非常贵,而且
当数据量达到机器的物理极限时,花再多的钱也买不到更强的机器了,此时选择横向扩展更为合适,但横向扩展带来的另外一个问题就是需要管理的机器太多。
MongoDB的设计采用横向扩展。面向文档的数据模型使它能很容易地在多台服务器之间进行数据分割。MongoDB能够自动处理跨集群的数据和负载,自动重新分配文档,以及将
用户的请求路由到正确的机器上。这样,开发者能够集中精力编写应用程序,而不需要考虑如何扩展的问题。如果一个集群需要更大的容量,只需要向集群添加新服务器,
MongoDB就会自动将现有的数据向新服务器传送

      d、丰硕的效果

MongoDB作为一款通用型数据库,除了能够创建、读取、更新和删除数据之外,还提供了一系列不断扩展的独特功能
#1、索引
支持通用二级索引,允许多种快速查询,且提供唯一索引、复合索引、地理空间索引、全文索引

#2、聚合
支持聚合管道,用户能通过简单的片段创建复杂的集合,并通过数据库自动优化

#3、特殊的集合类型
支持存在时间有限的集合,适用于那些将在某个时刻过期的数据,如会话session。类似地,MongoDB也支持固定大小的集合,用于保存近期数据,如日志

#4、文件存储
支持一种非常易用的协议,用于存储大文件和文件元数据。MongoDB并不具备一些在关系型数据库中很普遍的功能,如链接join和复杂的多行事务。省略
这些的功能是处于架构上的考虑,或者说为了得到更好的扩展性,因为在分布式系统中这两个功能难以高效地实

      e、杰出的质量

MongoDB的一个主要目标是提供卓越的性能,这很大程度上决定了MongoDB的设计。MongoDB把尽可能多的内存用作缓存cache,视图为每次查询自动选择正确的索引。
总之各方面的设计都旨在保持它的高性能
虽然MongoDB非常强大并试图保留关系型数据库的很多特性,但它并不追求具备关系型数据库的所有功能。只要有可能,数据库服务器就会将处理逻辑交给客户端。
这种精简方式的设计是MongoDB能够实现如此高性能的原因之一

十二、MongoDB基础

   
 a、文书档案是MongoDB的中央概念。文书档案正是键值对的一个静止集{‘msg’:’hello’,’foo’:3}。类似于python中的有序字典

需要注意的是:
#1、文档中的键/值对是有序的。
#2、文档中的值不仅可以是在双引号里面的字符串,还可以是其他几种数据类型(甚至可以是整个嵌入的文档)。
#3、MongoDB区分类型和大小写。
#4、MongoDB的文档不能有重复的键。
#5、文档中的值可以是多种不同的数据类型,也可以是一个完整的内嵌文档。文档的键是字符串。除了少数例外情况,键可以使用任意UTF-8字符。

文档键命名规范:
#1、键不能含有\0 (空字符)。这个字符用来表示键的结尾。
#2、.和$有特别的意义,只有在特定环境下才能使用。
#3、以下划线"_"开头的键是保留的(不是严格要求的)。

   
b、集合正是一组文档。如若将MongoDB中的二个文书档案比喻为关系型数据的一行,那么贰个成团正是一对一于一张表

#1、集合存在于数据库中,通常情况下为了方便管理,不同格式和类型的数据应该插入到不同的集合,但其实集合没有固定的结构,这意味着我们完全可以把不同格式和类型的数据统统插入一个集合中。

#2、组织子集合的方式就是使用“.”,分隔不同命名空间的子集合。
比如一个具有博客功能的应用可能包含两个集合,分别是blog.posts和blog.authors,这是为了使组织结构更清晰,这里的blog集合(这个集合甚至不需要存在)跟它的两个子集合没有任何关系。
在MongoDB中,使用子集合来组织数据非常高效,值得推荐

#3、当第一个文档插入时,集合就会被创建。合法的集合名:
集合名不能是空字符串""。
集合名不能含有\0字符(空字符),这个字符表示集合名的结尾。
集合名不能以"system."开头,这是为系统集合保留的前缀。
用户创建的集合名字不能含有保留字符。有些驱动程序的确支持在集合名里面包含,这是因为某些系统生成的集合中包含该字符。除非你要访问这种系统创建的集合,否则千万不要在名字里出现$。

    c、数据库:在MongoDB中,多少个文书档案组成集合,八个汇集能够整合数据库

数据库也通过名字来标识。数据库名可以是满足以下条件的任意UTF-8字符串:
#1、不能是空字符串("")。
#2、不得含有' '(空格)、.、$、/、\和\0 (空字符)。
#3、应全部小写。
#4、最多64字节。

有一些数据库名是保留的,可以直接访问这些有特殊作用的数据库。
#1、admin: 从身份认证的角度讲,这是“root”数据库,如果将一个用户添加到admin数据库,这个用户将自动获得所有数据库的权限。
再者,一些特定的服务器端命令也只能从admin数据库运行,如列出所有数据库或关闭服务器
#2、local: 这个数据库永远都不可以复制,且一台服务器上的所有本地集合都可以存储在这个数据库中
#3、config: MongoDB用于分片设置时,分片信息会存储在config数据库中

 
  d、强调:把数据库名添加到集合名前,得到集合的一点一滴限定名,即命名空间

例如:
如果要使用cms数据库中的blog.posts集合,这个集合的命名空间就是
cmd.blog.posts。命名空间的长度不得超过121个字节,且在实际使用中应该小于100个字节

十三、CRUD操作

    a、数据库操作

#1、增
use config #如果数据库不存在,则创建数据库,否则切换到指定数据库。

#2、查
show dbs #查看所有
可以看到,我们刚创建的数据库config并不在数据库的列表中, 要显示它,我们需要向config数据库插入一些数据。
db.table1.insert({'a':1})

#3、删
use config #先切换到要删的库下
db.dropDatabase() #删除当前库

   b、集合操作

#1、增
当第一个文档插入时,集合就会被创建
> use database1
switched to db database1
> db.table1.insert({'a':1})
WriteResult({ "nInserted" : 1 })
> db.table2.insert({'b':2})
WriteResult({ "nInserted" : 1 })

#2、查
> show tables
table1
table2

#3、删
> db.table1.drop()
true
> show tables
table2

   d、文书档案操作

   增:

   查:

澳门葡京备用网址 6

# SQL:=,!=,>,<,>=,<=
# MongoDB:{key:value}代表什么等于什么,"$ne","$gt","$lt","gte","lte",其中"$ne"能用于所有数据类型

#1、select * from db1.user where name = "alex";
db.user.find({'name':'alex'})

#2、select * from db1.user where name != "alex";
db.user.find({'name':{"$ne":'alex'}})

#3、select * from db1.user where id > 2;
db.user.find({'_id':{'$gt':2}})

#4、select * from db1.user where id < 3;
db.user.find({'_id':{'$lt':3}})

#5、select * from db1.user where id >= 2;
db.user.find({"_id":{"$gte":2,}})

#6、select * from db1.user where id <= 2;
db.user.find({"_id":{"$lte":2}})

比较运算
澳门葡京备用网址 7

# SQL:and,or,not
# MongoDB:字典中逗号分隔的多个条件是and关系,"$or"的条件放到[]内,"$not"

#1、select * from db1.user where id >= 2 and id < 4;
db.user.find({'_id':{"$gte":2,"$lt":4}})

#2、select * from db1.user where id >= 2 and age < 40;
db.user.find({"_id":{"$gte":2},"age":{"$lt":40}})

#3、select * from db1.user where id >= 5 or name = "alex";
db.user.find({
    "$or":[
        {'_id':{"$gte":5}},
        {"name":"alex"}
        ]
})

#4、select * from db1.user where id % 2=1;
db.user.find({'_id':{"$mod":[2,1]}})

#5、上题,取反
db.user.find({'_id':{"$not":{"$mod":[2,1]}}})

逻辑运算
澳门葡京备用网址 8

# SQL:in,not in
# MongoDB:"$in","$nin"

#1、select * from db1.user where age in (20,30,31);
db.user.find({"age":{"$in":[20,30,31]}})

#2、select * from db1.user where name not in ('alex','yuanhao');
db.user.find({"name":{"$nin":['alex','yuanhao']}}

分子运算
澳门葡京备用网址 9

# SQL: regexp 正则
# MongoDB: /正则表达/i

#1、select * from db1.user where name regexp '^j.*?(g|n)$';
db.user.find({'name':/^j.*?(g|n)$/i})

正则匹配
澳门葡京备用网址 10

#1、查看有dancing爱好的人
db.user.find({'hobbies':'dancing'})

#2、查看既有dancing爱好又有tea爱好的人
db.user.find({
    'hobbies':{
        "$all":['dancing','tea']
        }
})

#3、查看第4个爱好为tea的人
db.user.find({"hobbies.3":'tea'})

#4、查看所有人最后两个爱好
db.user.find({},{'hobbies':{"$slice":-2},"age":0,"_id":0,"name":0,"addr":0})

#5、查看所有人的第2个到第3个爱好
db.user.find({},{'hobbies':{"$slice":[1,2]},"age":0,"_id":0,"name":0,"addr":0})

> db.blog.find().pretty()
{
        "_id" : 1,
        "name" : "alex意外死亡的真相",
        "comments" : [
                {
                        "name" : "egon",
                        "content" : "alex是谁???",
                        "thumb" : 200
                },
                {
                        "name" : "wxx",
                        "content" : "我去,真的假的",
                        "thumb" : 300
                },
                {
                        "name" : "yxx",
                        "content" : "吃喝嫖赌抽,欠下两个亿",
                        "thumb" : 40
                },
                {
                        "name" : "egon",
                        "content" : "xxx",
                        "thumb" : 0
                }
        ]
}
db.blog.find({},{'comments':{"$slice":-2}}).pretty() #查询最后两个
db.blog.find({},{'comments':{"$slice":[1,2]}}).pretty() #查询1到2

查询数组
澳门葡京备用网址 11

# 排序:--1代表升序,-1代表降序
db.user.find().sort({"name":1,})
db.user.find().sort({"age":-1,'_id':1})

排序
澳门葡京备用网址 12

# 分页:--limit代表取多少个document,skip代表跳过前多少个document。 
db.user.find().sort({'age':1}).limit(1).skip(2)

分页
澳门葡京备用网址 13

# 获取数量
db.user.count({'age':{"$gt":30}}) 

--或者
db.user.find({'age':{"$gt":30}}).count()

收获数据
澳门葡京备用网址 14

#1、{'key':null} 匹配key的值为null或者没有这个key
db.t2.insert({'a':10,'b':111})
db.t2.insert({'a':20})
db.t2.insert({'b':null})

> db.t2.find({"b":null})
{ "_id" : ObjectId("5a5cc2a7c1b4645aad959e5a"), "a" : 20 }
{ "_id" : ObjectId("5a5cc2a8c1b4645aad959e5b"), "b" : null }

#2、查找所有
db.user.find() #等同于db.user.find({})
db.user.find().pretty()

#3、查找一个,与find用法一致,只是只取匹配成功的第一个
db.user.findOne({"_id":{"$gt":3}})

杂项

  改:

澳门葡京备用网址 15

update() 方法用于更新已存在的文档。语法格式如下:
db.collection.update(
   <query>,
   <update>,
   {
     upsert: <boolean>,
     multi: <boolean>,
     writeConcern: <document>
   }
)
参数说明:对比update db1.t1 set name='EGON',sex='Male' where name='egon' and age=18;

query : 相当于where条件。
update : update的对象和一些更新的操作符(如$,$inc...等,相当于set后面的
upsert : 可选,默认为false,代表如果不存在update的记录不更新也不插入,设置为true代表插入。
multi : 可选,默认为false,代表只更新找到的第一条记录,设为true,代表更新找到的全部记录。
writeConcern :可选,抛出异常的级别。

更新操作是不可分割的:若两个更新同时发送,先到达服务器的先执行,然后执行另外一个,不会破坏文档。

update语法介绍
澳门葡京备用网址 16

#注意:除非是删除,否则_id是始终不会变的
#1、覆盖式:
db.user.update({'age':20},{"name":"Wxx","hobbies_count":3})
是用{"_id":2,"name":"Wxx","hobbies_count":3}覆盖原来的记录

#2、一种最简单的更新就是用一个新的文档完全替换匹配的文档。这适用于大规模式迁移的情况。例如
var obj=db.user.findOne({"_id":2})

obj.username=obj.name+'SB'
obj.hobbies_count++
delete obj.age

db.user.update({"_id":2},obj)

覆盖式
澳门葡京备用网址 17

#设置:$set

通常文档只会有一部分需要更新。可以使用原子性的更新修改器,指定对文档中的某些字段进行更新。
更新修改器是种特殊的键,用来指定复杂的更新操作,比如修改、增加后者删除

#1、update db1.user set  name="WXX" where id = 2
db.user.update({'_id':2},{"$set":{"name":"WXX",}})

#2、没有匹配成功则新增一条{"upsert":true}
db.user.update({'_id':6},{"$set":{"name":"egon","age":18}},{"upsert":true})

#3、默认只改匹配成功的第一条,{"multi":改多条}
db.user.update({'_id':{"$gt":4}},{"$set":{"age":28}})
db.user.update({'_id':{"$gt":4}},{"$set":{"age":38}},{"multi":true})

#4、修改内嵌文档,把名字为alex的人所在的地址国家改成Japan
db.user.update({'name':"alex"},{"$set":{"addr.country":"Japan"}})

#5、把名字为alex的人的地2个爱好改成piao
db.user.update({'name':"alex"},{"$set":{"hobbies.1":"piao"}})

#6、删除alex的爱好,$unset
db.user.update({'name':"alex"},{"$unset":{"hobbies":""}})

设置:$set
澳门葡京备用网址 18

往数组内添加元素:$push
#1、为名字为yuanhao的人添加一个爱好read
db.user.update({"name":"yuanhao"},{"$push":{"hobbies":"read"}})

#2、为名字为yuanhao的人一次添加多个爱好tea,dancing
db.user.update({"name":"yuanhao"},{"$push":{
    "hobbies":{"$each":["tea","dancing"]}
}})

按照位置且只能从开头或结尾删除元素:$pop
#3、{"$pop":{"key":1}} 从数组末尾删除一个元素

db.user.update({"name":"yuanhao"},{"$pop":{
    "hobbies":1}
})

#4、{"$pop":{"key":-1}} 从头部删除
db.user.update({"name":"yuanhao"},{"$pop":{
    "hobbies":-1}
})

#5、按照条件删除元素,:"$pull" 把符合条件的统统删掉,而$pop只能从两端删
db.user.update({'addr.country':"China"},{"$pull":{
    "hobbies":"read"}
},
{
    "multi":true
}
)

#累加删减数组内成分

 删:

澳门葡京备用网址 19

#1、删除多个中的第一个
db.user.deleteOne({ 'age': 8 })

#2、删除国家为China的全部
db.user.deleteMany( {'addr.country': 'China'} ) 

#3、删除全部
db.user.deleteMany({})

View Code

   e、聚合

如果你有数据存储在MongoDB中,你想做的可能就不仅仅是将数据提取出来那么简单了;你可能希望对数据进行分析并加以利用。MongoDB提供了以下聚合工具:
#1、聚合框架
#2、MapReduce(详见MongoDB权威指南)
#3、几个简单聚合命令:count、distinct和group。(详见MongoDB权威指南)

#聚合框架:
可以使用多个构件创建一个管道,上一个构件的结果传给下一个构件。
这些构件包括(括号内为构件对应的操作符):筛选($match)、投射($project)、分组($group)、排序($sort)、限制($limit)、跳过($skip)
不同的管道操作符可以任意组合,重复使用

十④ 、爬虫品质

   a、背景知识

   爬虫的本质就是一个socket客户端与服务端的通信过程,如果我们有多个url待爬取,只用一个线程且采用串行的方式执行,
那只能等待爬取一个结束后才能继续下一个,效率会非常低。需要强调的是:对于单线程下串行N个任务,并不完全等同于低效,
如果这N个任务都是纯计算的任务,那么该线程对cpu的利用率仍然会很高,之所以单线程下串行多个爬虫任务低效,
是因为爬虫任务是明显的IO密集型程序。

   b、同步、异步、回调机制

 
 c、联合调用:即提交三个职分后就在原地等待义务实现,等到获得职分的结果后再持续下一行代码,功用低下

十五、高性能

 上述无论哪种解决方案其实没有解决一个性能相关的问题:IO阻塞,无论是多进程还是多线程,在遇到IO阻塞时都会被操作系统强行剥夺走CPU的执行权限,
程序的执行效率因此就降低了下来。
    解决这一问题的关键在于,我们自己从应用程序级别检测IO阻塞然后切换到我们自己程序的其他任务执行,这样把我们程序的IO降到最低,
我们的程序处于就绪态就会增多,以此来迷惑操作系统,操作系统便以为我们的程序是IO比较少的程序,从而会尽可能多的分配CPU给我们,这样也就达到了提升程序执行效率的目的

 
 a、在python3.3后头新增了asyncio模块,能够帮大家检查和测试IO(只可以是互连网IO),完成应用程序级别的切换

 
 b、
但asyncio模块只可以发tcp级别的呼吁,无法发http协议,由此,在大家供给发送http请求的时候,须要我们自定义http报头**

** 
 c、
自定义http报头多少有点麻烦,于是有了aiohttp模块,专门帮我们封装http报头,然后我们还索要用asyncio检查和测试IO完毕切换**

** 
 d、其余,还足以将requests.get函数字传送给asyncio,就能够被检查和测试了**

**   e、再有从前在协程时介绍的gevent模块**

**   f、封装了gevent+requests模块的grequests模块**

** 
 g、twisted:是贰个互连网框架,当中三个职能是发送异步请求,检查和测试IO并活动切换**

**十六、scrapy**

**   a、介绍**

   Scrapy一个开源和协作的框架,其最初是为了页面抓取 (更确切来说, 网络抓取 )所设计的,使用它可以以快速、简单、可扩展的方式从网站中提取所需的数据。
但目前Scrapy的用途十分广泛,可用于如数据挖掘、监测和自动化测试等领域,也可以应用在获取API所返回的数据(例如 Amazon Associates Web Services ) 或者通用的网络爬虫。
    Scrapy 是基于twisted框架开发而来,twisted是一个流行的事件驱动的python网络框架。因此Scrapy使用了一种非阻塞(又名异步)的代码来实现并发

   b、安装

#Windows平台
    1、pip3 install wheel #安装后,便支持通过wheel文件安装软件,wheel文件官网:https://www.lfd.uci.edu/~gohlke/pythonlibs
    3、pip3 install lxml
    4、pip3 install pyopenssl
    5、下载并安装pywin32:https://sourceforge.net/projects/pywin32/files/pywin32/
    6、下载twisted的wheel文件:http://www.lfd.uci.edu/~gohlke/pythonlibs/#twisted
    7、执行pip3 install 下载目录\Twisted-17.9.0-cp36-cp36m-win_amd64.whl
    8、pip3 install scrapy

#Linux平台
    1、pip3 install scrapy

    c、命令行工具

#1 查看帮助
    scrapy -h
    scrapy <command> -h

#2 有两种命令:其中Project-only必须切到项目文件夹下才能执行,而Global的命令则不需要
    Global commands:
        startproject #创建项目
        genspider    #创建爬虫程序
        settings     #如果是在项目目录下,则得到的是该项目的配置
        runspider    #运行一个独立的python文件,不必创建项目
        shell        #scrapy shell url地址  在交互式调试,如选择器规则正确与否
        fetch        #独立于程单纯地爬取一个页面,可以拿到请求头
        view         #下载完毕后直接弹出浏览器,以此可以分辨出哪些数据是ajax请求
        version      #scrapy version 查看scrapy的版本,scrapy version -v查看scrapy依赖库的版本
    Project-only commands:
        crawl        #运行爬虫,必须创建项目才行,确保配置文件中ROBOTSTXT_OBEY = False
        check        #检测项目中有无语法错误
        list         #列出项目中所包含的爬虫名
        edit         #编辑器,一般不用
        parse        #scrapy parse url地址 --callback 回调函数  #以此可以验证我们的回调函数是否正确
        bench        #scrapy bentch压力测试

#3 官网链接
    https://docs.scrapy.org/en/latest/topics/commands.html

   d、文本说明:

  • scrapy.cfg
     项目的主配置消息,用来安排scrapy时利用,爬虫相关的配置音信在settings.py文件中。
  • items.py    设置数据存款和储蓄模板,用于结构化数据,如:Django的Model
  • pipelines    数据处理作为,如:一般结构化的数额持久化
  • settings.py
    配置文件,如:递归的层数、并发数,延迟下载等。强调:配置文件的选项必须大写不然即是无效,正确写法USEHaval_AGENT=’xxxx’
  • spiders      爬虫目录,如:创立文件,编写爬虫规则

十七、Spiders**

    a、介绍

#1、Spiders是由一系列类(定义了一个网址或一组网址将被爬取)组成,具体包括如何执行爬取任务并且如何从页面中提取结构化的数据。

#2、换句话说,Spiders是你为了一个特定的网址或一组网址自定义爬取和解析页面行为的地方

    b、Spiders会循环做如下事情

#1、生成初始的Requests来爬取第一个URLS,并且标识一个回调函数
第一个请求定义在start_requests()方法内默认从start_urls列表中获得url地址来生成Request请求,默认的回调函数是parse方法。回调函数在下载完成返回response时自动触发

#2、在回调函数中,解析response并且返回值
返回值可以4种:
        包含解析数据的字典
        Item对象
        新的Request对象(新的Requests也需要指定一个回调函数)
        或者是可迭代对象(包含Items或Request)

#3、在回调函数中解析页面内容
通常使用Scrapy自带的Selectors,但很明显你也可以使用Beutifulsoup,lxml或其他你爱用啥用啥。

#4、最后,针对返回的Items对象将会被持久化到数据库
通过Item Pipeline组件存到数据库:https://docs.scrapy.org/en/latest/topics/item-pipeline.html#topics-item-pipeline)
或者导出到不同的文件(通过Feed exports:https://docs.scrapy.org/en/latest/topics/feed-exports.html#topics-feed-exports)
复制代码

    e、Spiders总共提供了五品种

#1、scrapy.spiders.Spider #scrapy.Spider等同于scrapy.spiders.Spider
#2、scrapy.spiders.CrawlSpider
#3、scrapy.spiders.XMLFeedSpider
#4、scrapy.spiders.CSVFeedSpider
#5、scrapy.spiders.SitemapSpider

    f、导入使用

    g、class scrapy.spiders.spider

这是最简单的spider类,任何其他的spider类都需要继承它(包含你自己定义的)。

该类不提供任何特殊的功能,它仅提供了一个默认的start_requests方法默认从start_urls中读取url地址发送requests请求,并且默认parse作为回调函数

十八、Selectors

#1 //与/
#2 text
#3、extract与extract_first:从selector对象中解出内容
#4、属性:xpath的属性加前缀@
#4、嵌套查找
#5、设置默认值
#4、按照属性查找
#5、按照属性模糊查找
#6、正则表达式
#7、xpath相对路径
#8、带变量的xpath

 

 

 

 

 

壹 、爬虫之requests
a、介绍:
使用requests能够效仿浏览器的伏乞,比起从前使用的urllib,requests模块的api特别便捷…

对此爬虫,python进行并发抓取的落到实处格局根本有以下二种:进度,线程,协程。

官网链接:https://docs.scrapy.org/en/latest/topics/architecture.html

   
a、介绍:应用requests能够萧规曹随浏览器的请求,比起以前运用的urllib,requests模块的api特别便捷(本质正是包装了urllib3)

性子的成本首要在IO请求中,当单进度单线程方式下请求U汉兰达L时必定会挑起等待,从而使得请求全部变慢。

质量相关

在编排爬虫时,性能的消耗重要在IO请求中,当单进度单线程方式下请求URL时一定会引起等待,从而使得请求全部变慢。

澳门葡京备用网址 20澳门葡京备用网址 21

import requests

def fetch_async(url):
    response = requests.get(url)
    return response


url_list = ['http://www.github.com', 'http://www.bing.com']

for url in url_list:
    fetch_async(url)

1.协助进行执行

澳门葡京备用网址 22澳门葡京备用网址 23

from concurrent.futures import ThreadPoolExecutor
import requests


def fetch_async(url):
    response = requests.get(url)
    return response


url_list = ['http://www.github.com', 'http://www.bing.com']
pool = ThreadPoolExecutor(5)
for url in url_list:
    pool.submit(fetch_async, url)
pool.shutdown(wait=True)

2.四线程执行

澳门葡京备用网址 24澳门葡京备用网址 25

from concurrent.futures import ThreadPoolExecutor
import requests

def fetch_async(url):
    response = requests.get(url)
    return response


def callback(future):
    print(future.result())


url_list = ['http://www.github.com', 'http://www.bing.com']
pool = ThreadPoolExecutor(5)
for url in url_list:
    v = pool.submit(fetch_async, url)
    v.add_done_callback(callback)
pool.shutdown(wait=True)

2.多线程+回调函数执行

澳门葡京备用网址 26澳门葡京备用网址 27

from concurrent.futures import ProcessPoolExecutor
import requests

def fetch_async(url):
    response = requests.get(url)
    return response


url_list = ['http://www.github.com', 'http://www.bing.com']
pool = ProcessPoolExecutor(5)
for url in url_list:
    pool.submit(fetch_async, url)
pool.shutdown(wait=True)

3.多进程执行

澳门葡京备用网址 28澳门葡京备用网址 29

from concurrent.futures import ProcessPoolExecutor
import requests


def fetch_async(url):
    response = requests.get(url)
    return response


def callback(future):
    print(future.result())


url_list = ['http://www.github.com', 'http://www.bing.com']
pool = ProcessPoolExecutor(5)
for url in url_list:
    v = pool.submit(fetch_async, url)
    v.add_done_callback(callback)
pool.shutdown(wait=True)

3.多进度+回调函数执行

经过上述代码均能够形成对请求品质的滋长,对于多线程和多开展的瑕疵是在IO阻塞时会造成了线程和经过的浪费,所以异步IO回事首要选拔:

澳门葡京备用网址 30澳门葡京备用网址 31

import asyncio


@asyncio.coroutine
def func1():
    print('before...func1......')
    yield from asyncio.sleep(5)
    print('end...func1......')


tasks = [func1(), func1()]

loop = asyncio.get_event_loop()
loop.run_until_complete(asyncio.gather(*tasks))
loop.close()

1.asyncio示例1

澳门葡京备用网址 32澳门葡京备用网址 33

import asyncio


@asyncio.coroutine
def fetch_async(host, url='/'):
    print(host, url)
    reader, writer = yield from asyncio.open_connection(host, 80)

    request_header_content = """GET %s HTTP/1.0\r\nHost: %s\r\n\r\n""" % (url, host,)
    request_header_content = bytes(request_header_content, encoding='utf-8')

    writer.write(request_header_content)
    yield from writer.drain()
    text = yield from reader.read()
    print(host, url, text)
    writer.close()

tasks = [
    fetch_async('www.cnblogs.com', '/wupeiqi/'),
    fetch_async('dig.chouti.com', '/pic/show?nid=4073644713430508&lid=10273091')
]

loop = asyncio.get_event_loop()
results = loop.run_until_complete(asyncio.gather(*tasks))
loop.close()

1.asyncio示例2

澳门葡京备用网址 34澳门葡京备用网址 35

import aiohttp
import asyncio


@asyncio.coroutine
def fetch_async(url):
    print(url)
    response = yield from aiohttp.request('GET', url)
    # data = yield from response.read()
    # print(url, data)
    print(url, response)
    response.close()


tasks = [fetch_async('http://www.google.com/'), fetch_async('http://www.chouti.com/')]

event_loop = asyncio.get_event_loop()
results = event_loop.run_until_complete(asyncio.gather(*tasks))
event_loop.close()

2.asyncio + aiohttp

澳门葡京备用网址 36澳门葡京备用网址 37

import asyncio
import requests


@asyncio.coroutine
def fetch_async(func, *args):
    loop = asyncio.get_event_loop()
    future = loop.run_in_executor(None, func, *args)
    response = yield from future
    print(response.url, response.content)


tasks = [
    fetch_async(requests.get, 'http://www.cnblogs.com/wupeiqi/'),
    fetch_async(requests.get, 'http://dig.chouti.com/pic/show?nid=4073644713430508&lid=10273091')
]

loop = asyncio.get_event_loop()
results = loop.run_until_complete(asyncio.gather(*tasks))
loop.close()

3.asyncio + requests

澳门葡京备用网址 38澳门葡京备用网址 39

import gevent

import requests
from gevent import monkey

monkey.patch_all()


def fetch_async(method, url, req_kwargs):
    print(method, url, req_kwargs)
    response = requests.request(method=method, url=url, **req_kwargs)
    print(response.url, response.content)

# ##### 发送请求 #####
gevent.joinall([
    gevent.spawn(fetch_async, method='get', url='https://www.python.org/', req_kwargs={}),
    gevent.spawn(fetch_async, method='get', url='https://www.yahoo.com/', req_kwargs={}),
    gevent.spawn(fetch_async, method='get', url='https://github.com/', req_kwargs={}),
])

# ##### 发送请求(协程池控制最大协程数量) #####
# from gevent.pool import Pool
# pool = Pool(None)
# gevent.joinall([
#     pool.spawn(fetch_async, method='get', url='https://www.python.org/', req_kwargs={}),
#     pool.spawn(fetch_async, method='get', url='https://www.yahoo.com/', req_kwargs={}),
#     pool.spawn(fetch_async, method='get', url='https://www.github.com/', req_kwargs={}),
# ])

4.gevent + requests

澳门葡京备用网址 40澳门葡京备用网址 41

import grequests


request_list = [
    grequests.get('http://httpbin.org/delay/1', timeout=0.001),
    grequests.get('http://fakedomain/'),
    grequests.get('http://httpbin.org/status/500')
]


# ##### 执行并获取响应列表 #####
# response_list = grequests.map(request_list)
# print(response_list)


# ##### 执行并获取响应列表(处理异常) #####
# def exception_handler(request, exception):
# print(request,exception)
#     print("Request failed")

# response_list = grequests.map(request_list, exception_handler=exception_handler)
# print(response_list)

5.grequests

澳门葡京备用网址 42澳门葡京备用网址 43

from twisted.web.client import getPage, defer
from twisted.internet import reactor


def all_done(arg):
    reactor.stop()


def callback(contents):
    print(contents)


deferred_list = []

url_list = ['http://www.bing.com', 'http://www.baidu.com', ]
for url in url_list:
    deferred = getPage(bytes(url, encoding='utf8'))
    deferred.addCallback(callback)
    deferred_list.append(deferred)

dlist = defer.DeferredList(deferred_list)
dlist.addBoth(all_done)

reactor.run()

6.Twisted示例

澳门葡京备用网址 44澳门葡京备用网址 45

from tornado.httpclient import AsyncHTTPClient
from tornado.httpclient import HTTPRequest
from tornado import ioloop


def handle_response(response):
    """
    处理返回值内容(需要维护计数器,来停止IO循环),调用 ioloop.IOLoop.current().stop()
    :param response: 
    :return: 
    """
    if response.error:
        print("Error:", response.error)
    else:
        print(response.body)


def func():
    url_list = [
        'http://www.baidu.com',
        'http://www.bing.com',
    ]
    for url in url_list:
        print(url)
        http_client = AsyncHTTPClient()
        http_client.fetch(HTTPRequest(url), handle_response)


ioloop.IOLoop.current().add_callback(func)
ioloop.IOLoop.current().start()

7.Tornado

澳门葡京备用网址 46澳门葡京备用网址 47

from twisted.internet import reactor
from twisted.web.client import getPage
import urllib.parse


def one_done(arg):
    print(arg)
    reactor.stop()

post_data = urllib.parse.urlencode({'check_data': 'adf'})
post_data = bytes(post_data, encoding='utf8')
headers = {b'Content-Type': b'application/x-www-form-urlencoded'}
response = getPage(bytes('http://dig.chouti.com/login', encoding='utf8'),
                   method=bytes('POST', encoding='utf8'),
                   postdata=post_data,
                   cookies={},
                   headers=headers)
response.addBoth(one_done)

reactor.run()

Twisted更多

以上均是Python内置以及第①方模块提供异步IO请求模块,使用方便大大进步效能,而对于异步IO请求的面目则是【非阻塞Socket】+【IO多路复用】:

澳门葡京备用网址 48澳门葡京备用网址 49

import select
import socket
import time


class AsyncTimeoutException(TimeoutError):
    """
    请求超时异常类
    """

    def __init__(self, msg):
        self.msg = msg
        super(AsyncTimeoutException, self).__init__(msg)


class HttpContext(object):
    """封装请求和相应的基本数据"""

    def __init__(self, sock, host, port, method, url, data, callback, timeout=5):
        """
        sock: 请求的客户端socket对象
        host: 请求的主机名
        port: 请求的端口
        port: 请求的端口
        method: 请求方式
        url: 请求的URL
        data: 请求时请求体中的数据
        callback: 请求完成后的回调函数
        timeout: 请求的超时时间
        """
        self.sock = sock
        self.callback = callback
        self.host = host
        self.port = port
        self.method = method
        self.url = url
        self.data = data

        self.timeout = timeout

        self.__start_time = time.time()
        self.__buffer = []

    def is_timeout(self):
        """当前请求是否已经超时"""
        current_time = time.time()
        if (self.__start_time + self.timeout) < current_time:
            return True

    def fileno(self):
        """请求sockect对象的文件描述符,用于select监听"""
        return self.sock.fileno()

    def write(self, data):
        """在buffer中写入响应内容"""
        self.__buffer.append(data)

    def finish(self, exc=None):
        """在buffer中写入响应内容完成,执行请求的回调函数"""
        if not exc:
            response = b''.join(self.__buffer)
            self.callback(self, response, exc)
        else:
            self.callback(self, None, exc)

    def send_request_data(self):
        content = """%s %s HTTP/1.0\r\nHost: %s\r\n\r\n%s""" % (
            self.method.upper(), self.url, self.host, self.data,)

        return content.encode(encoding='utf8')


class AsyncRequest(object):
    def __init__(self):
        self.fds = []
        self.connections = []

    def add_request(self, host, port, method, url, data, callback, timeout):
        """创建一个要请求"""
        client = socket.socket()
        client.setblocking(False)
        try:
            client.connect((host, port))
        except BlockingIOError as e:
            pass
            # print('已经向远程发送连接的请求')
        req = HttpContext(client, host, port, method, url, data, callback, timeout)
        self.connections.append(req)
        self.fds.append(req)

    def check_conn_timeout(self):
        """检查所有的请求,是否有已经连接超时,如果有则终止"""
        timeout_list = []
        for context in self.connections:
            if context.is_timeout():
                timeout_list.append(context)
        for context in timeout_list:
            context.finish(AsyncTimeoutException('请求超时'))
            self.fds.remove(context)
            self.connections.remove(context)

    def running(self):
        """事件循环,用于检测请求的socket是否已经就绪,从而执行相关操作"""
        while True:
            r, w, e = select.select(self.fds, self.connections, self.fds, 0.05)

            if not self.fds:
                return

            for context in r:
                sock = context.sock
                while True:
                    try:
                        data = sock.recv(8096)
                        if not data:
                            self.fds.remove(context)
                            context.finish()
                            break
                        else:
                            context.write(data)
                    except BlockingIOError as e:
                        break
                    except TimeoutError as e:
                        self.fds.remove(context)
                        self.connections.remove(context)
                        context.finish(e)
                        break

            for context in w:
                # 已经连接成功远程服务器,开始向远程发送请求数据
                if context in self.fds:
                    data = context.send_request_data()
                    context.sock.sendall(data)
                    self.connections.remove(context)

            self.check_conn_timeout()


if __name__ == '__main__':
    def callback_func(context, response, ex):
        """
        :param context: HttpContext对象,内部封装了请求相关信息
        :param response: 请求响应内容
        :param ex: 是否出现异常(如果有异常则值为异常对象;否则值为None)
        :return:
        """
        print(context, response, ex)

    obj = AsyncRequest()
    url_list = [
        {'host': 'www.google.com', 'port': 80, 'method': 'GET', 'url': '/', 'data': '', 'timeout': 5,
         'callback': callback_func},
        {'host': 'www.baidu.com', 'port': 80, 'method': 'GET', 'url': '/', 'data': '', 'timeout': 5,
         'callback': callback_func},
        {'host': 'www.bing.com', 'port': 80, 'method': 'GET', 'url': '/', 'data': '', 'timeout': 5,
         'callback': callback_func},
    ]
    for item in url_list:
        print(item)
        obj.add_request(**item)

    obj.running()

史上最牛逼的异步IO模块

   
b、注意:requests发送请求是将网页内容下载来未来,并不会履行js代码,那供给大家友好分析指标站点然后发起新的requests请求

一 多进度执行

Scrapy

    c、安装:pip3 install requests

能够完结产出,但是,请求发送出去后和重临之前,中间时代进度空闲

一 介绍

    Scrapy一个开源和搭档的框架,其早期是为着页面抓取 (更妥善来说,
互连网抓取
)所设计的,使用它可以以飞快、不难、可扩展的措施从网站中领取所需的多少。但当下Scrapy的用处丰盛宽广,可用来如数据挖掘、监测和自动化测试等世界,也足以动用在取得API所重回的数量(例如
亚马逊 Associates Web Services ) 可能通用的互连网爬虫。

    Scrapy
是依据twisted框架开发而来,twisted是贰个盛行的事件驱动的python网络框架。因而Scrapy使用了一种非阻塞(又名异步)的代码来促成产出。全部架构大约如下Scrapy是3个为了爬取网站数据,提取结构性数据而编辑的利用框架。
其得以选用在数额挖掘,音讯处理或存款和储蓄历史数据等一种种的顺序中。

Scrapy 使用了 Twisted异步互连网库来处理互联网通信。整体架构大致如下

澳门葡京备用网址 50

Scrapy首要回顾了以下组件:

  • 引擎(Scrapy)
    用来拍卖整个类别的数据流处理, 触发工作(框架焦点)
  • 调度器(Scheduler)
    用来经受引擎发过来的伸手, 压入队列中, 并在内燃机再一次恳请的时候再次来到.
    能够想像成2个UPRADOL(抓取网页的网址或许说是链接)的先期队列,
    由它来支配下二个要抓取的网址是如何, 同时去除重复的网址
  • 下载器(Downloader)
    用来下载网页内容,
    并将网页内容重返给蜘蛛(Scrapy下载器是树立在twisted这一个便捷的异步模型上的)
  • 爬虫(Spiders)
    爬虫是生死攸关办事的, 用于从一定的网页中领到自身要求的新闻,
    即所谓的实业(Item)。用户也能够从中提取出链接,让Scrapy继续抓取下一个页面
  • 品类管道(Pipeline)
    肩负处理爬虫从网页中抽取的实业,主要的成效是持久化实体、验证实体的有用、清除不须要的消息。当页面被爬虫解析后,将被发送到项目管道,并透过多少个特定的顺序处理数量。
  • 下载器中间件(Downloader Middlewares)
    座落Scrapy引擎和下载器之间的框架,主要是拍卖Scrapy引擎与下载器之间的乞求及响应。
  • 爬虫中间件(Spider Middlewares)
    介于Scrapy引擎和爬虫之间的框架,主要工作是拍卖蜘蛛的响应输入和伸手输出。
  • 调度中间件(Scheduler Middewares)
    介于Scrapy引擎和调度之间的中间件,从Scrapy引擎发送到调度的呼吁和响应。

Scrapy运转流程大致如下:

  1. 斯特林发动机从调度器中取出二个链接(UOdysseyL)用于接下去的抓取
  2. 斯特林发动机把UTiguanL封装成一个呼吁(Request)传给下载器
  3. 下载器把财富下载下来,并封装成应答包(Response)
  4. 爬虫解析Response
  5. 浅析出实体(Item),则交给实体管道展开更为的处理
  6. 剖析出的是链接(U福特ExplorerL),则把USportageL交给调度器等待抓取

    d、种种请求情势,常用的是requests.get()和requets.post()

编写格局:
1- 多进度向来回随处理

一、安装

#Windows平台
    1、pip3 install wheel #安装后,便支持通过wheel文件安装软件,wheel文件官网:https://www.lfd.uci.edu/~gohlke/pythonlibs
    3、pip3 install lxml
    4、pip3 install pyopenssl
    5、下载并安装pywin32:https://sourceforge.net/projects/pywin32/files/pywin32/
    6、下载twisted的wheel文件:http://www.lfd.uci.edu/~gohlke/pythonlibs/#twisted
    7、执行pip3 install 下载目录\Twisted-17.9.0-cp36-cp36m-win_amd64.whl
    8、pip3 install scrapy

#Linux平台
    1、pip3 install scrapy

 

二、基于get请求

澳门葡京备用网址 51澳门葡京备用网址 52

② 、基本选拔

    a、基本请求

 1 from concurrent.futures import ProcessPoolExecutor
 2 import requests
 3 import time
 4 
 5 def task(url):
 6     response = requests.get(url)
 7     print(url,response)
 8     # 写正则表达式
 9     return response
10 
11 pool = ProcessPoolExecutor(7)
12 url_list = [
13     'http://www.cnblogs.com/wupeiqi',
14     'http://huaban.com/favorite/beauty/',
15     'http://www.bing.com',
16     'http://www.zhihu.com',
17     'http://www.sina.com',
18     'http://www.baidu.com',
19     'http://www.autohome.com.cn',
20 ]
21 
22 for url in url_list:
23     pool.submit(task,url)
24 
25 pool.shutdown(wait=True)

1. 核心命令

#1 查看帮助
    scrapy -h
    scrapy <command> -h

#2 有两种命令:其中Project-only必须切到项目文件夹下才能执行,而Global的命令则不需要
    Global commands:
        startproject #创建项目
        genspider    #创建爬虫程序 
                            如:
                              scrapy gensipider -t basic oldboy oldboy.com
                              scrapy gensipider -t xmlfeed autohome autohome.com.cn
        settings     #如果是在项目目录下,则得到的是该项目的配置
        runspider    #运行一个独立的python文件,不必创建项目
        shell        #scrapy shell url地址  在交互式调试,如选择器规则正确与否
        fetch        #独立于程单纯地爬取一个页面,可以拿到请求头
        view         #下载完毕后直接弹出浏览器,以此可以分辨出哪些数据是ajax请求
        version      #scrapy version 查看scrapy的版本,scrapy version -v查看scrapy依赖库的版本
    Project-only commands:
        crawl        #运行爬虫,必须创建项目才行,确保配置文件中ROBOTSTXT_OBEY = False
        check        #检测项目中有无语法错误
        list         #列出项目中所包含的爬虫名
        edit         #编辑器,一般不用
        parse        #scrapy parse url地址 --callback 回调函数  #以此可以验证我们的回调函数是否正确
        bench        #scrapy bentch压力测试

#3 官网链接
    https://docs.scrapy.org/en/latest/topics/commands.html        

澳门葡京备用网址 53澳门葡京备用网址 54

#1、执行全局命令:请确保不在某个项目的目录下,排除受该项目配置的影响
scrapy startproject MyProject

cd MyProject
scrapy genspider baidu www.baidu.com

scrapy settings --get XXX #如果切换到项目目录下,看到的则是该项目的配置

scrapy runspider baidu.py

scrapy shell https://www.baidu.com
    response
    response.status
    response.body
    view(response)

scrapy view https://www.taobao.com #如果页面显示内容不全,不全的内容则是ajax请求实现的,以此快速定位问题

scrapy fetch --nolog --headers https://www.taobao.com

scrapy version #scrapy的版本

scrapy version -v #依赖库的版本


#2、执行项目命令:切到项目目录下
scrapy crawl baidu
scrapy check
scrapy list
scrapy parse http://quotes.toscrape.com/ --callback parse
scrapy bench


示范用法

演示用法

 

2.项目布局以及爬虫应用简介

project_name/
   scrapy.cfg
   project_name/
       __init__.py
       items.py
       pipelines.py
       settings.py
       spiders/
           __init__.py
           爬虫1.py
           爬虫2.py
           爬虫3.py

文件表达:

  • scrapy.cfg
     项目标主配置音讯。(真正爬虫相关的陈设音讯在settings.py文件中)
  • items.py    设置数据存款和储蓄模板,用于结构化数据,如:Django的Model
  • pipelines    数据处理作为,如:一般结构化的多寡持久化
  • settings.py 配置文件,如:递归的层数、并发数,延迟下载等
  • spiders      爬虫目录,如:创设文件,编写爬虫规则

注意:一般成立爬虫文件时,以网站域名命名

澳门葡京备用网址 55澳门葡京备用网址 56

import scrapy

class XiaoHuarSpider(scrapy.spiders.Spider):
    name = "xiaohuar"                            # 爬虫名称 *****
    allowed_domains = ["xiaohuar.com"]  # 允许的域名
    start_urls = [
        "http://www.xiaohuar.com/hua/",   # 其实URL
    ]

    def parse(self, response):
        # 访问起始URL并获取结果后的回调函数

爬虫1.py

澳门葡京备用网址 57澳门葡京备用网址 58

import sys,os
sys.stdout=io.TextIOWrapper(sys.stdout.buffer,encoding='gb18030')

关于windows编码

3. 小规模试制牛刀

 

import scrapy
from scrapy.selector import HtmlXPathSelector
from scrapy.http.request import Request


class DigSpider(scrapy.Spider):
    # 爬虫应用的名称,通过此名称启动爬虫命令
    name = "dig"

    # 允许的域名
    allowed_domains = ["chouti.com"]

    # 起始URL
    start_urls = [
        'http://dig.chouti.com/',
    ]

    has_request_set = {}

    def parse(self, response):
        print(response.url)

        hxs = HtmlXPathSelector(response)
        page_list = hxs.select('//div[@id="dig_lcpage"]//a[re:test(@href, "/all/hot/recent/\d+")]/@href').extract()
        for page in page_list:
            page_url = 'http://dig.chouti.com%s' % page
            key = self.md5(page_url)
            if key in self.has_request_set:
                pass
            else:
                self.has_request_set[key] = page_url
                obj = Request(url=page_url, method='GET', callback=self.parse)
                yield obj

    @staticmethod
    def md5(val):
        import hashlib
        ha = hashlib.md5()
        ha.update(bytes(val, encoding='utf-8'))
        key = ha.hexdigest()
        return key

  

实践此爬虫文件,则在顶峰进入项目目录执行如下命令:

scrapy crawl dig --nolog

对此上述代码首要之处在于:

  • Request是三个卷入用户请求的类,在回调函数中yield该对象表示继续走访
  • HtmlXpathSelector用于结构化HTML代码并提供选用器功用

4. 选择器

#1 //与/
#2 text
#3、extract与extract_first:从selector对象中解出内容
#4、属性:xpath的属性加前缀@
#4、嵌套查找
#5、设置默认值
#4、按照属性查找
#5、按照属性模糊查找
#6、正则表达式
#7、xpath相对路径
#8、带变量的xpath

澳门葡京备用网址 59澳门葡京备用网址 60

response.selector.css()
response.selector.xpath()
可简写为
response.css()
response.xpath()

#1 //与/
response.xpath('//body/a/')#
response.css('div a::text')

>>> response.xpath('//body/a') #开头的//代表从整篇文档中寻找,body之后的/代表body的儿子
[]
>>> response.xpath('//body//a') #开头的//代表从整篇文档中寻找,body之后的//代表body的子子孙孙
[<Selector xpath='//body//a' data='<a href="image1.html">Name: My image 1 <'>, <Selector xpath='//body//a' data='<a href="image2.html">Name: My image 2 <'>, <Selector xpath='//body//a' data='<a href="
image3.html">Name: My image 3 <'>, <Selector xpath='//body//a' data='<a href="image4.html">Name: My image 4 <'>, <Selector xpath='//body//a' data='<a href="image5.html">Name: My image 5 <'>]

#2 text
>>> response.xpath('//body//a/text()')
>>> response.css('body a::text')

#3、extract与extract_first:从selector对象中解出内容
>>> response.xpath('//div/a/text()').extract()
['Name: My image 1 ', 'Name: My image 2 ', 'Name: My image 3 ', 'Name: My image 4 ', 'Name: My image 5 ']
>>> response.css('div a::text').extract()
['Name: My image 1 ', 'Name: My image 2 ', 'Name: My image 3 ', 'Name: My image 4 ', 'Name: My image 5 ']

>>> response.xpath('//div/a/text()').extract_first()
'Name: My image 1 '
>>> response.css('div a::text').extract_first()
'Name: My image 1 '

#4、属性:xpath的属性加前缀@
>>> response.xpath('//div/a/@href').extract_first()
'image1.html'
>>> response.css('div a::attr(href)').extract_first()
'image1.html'

#4、嵌套查找
>>> response.xpath('//div').css('a').xpath('@href').extract_first()
'image1.html'

#5、设置默认值
>>> response.xpath('//div[@id="xxx"]').extract_first(default="not found")
'not found'

#4、按照属性查找
response.xpath('//div[@id="images"]/a[@href="image3.html"]/text()').extract()
response.css('#images a[@href="image3.html"]/text()').extract()

#5、按照属性模糊查找
response.xpath('//a[contains(@href,"image")]/@href').extract()
response.css('a[href*="image"]::attr(href)').extract()

response.xpath('//a[contains(@href,"image")]/img/@src').extract()
response.css('a[href*="imag"] img::attr(src)').extract()

response.xpath('//*[@href="image1.html"]')
response.css('*[href="image1.html"]')

#6、正则表达式
response.xpath('//a/text()').re(r'Name: (.*)')
response.xpath('//a/text()').re_first(r'Name: (.*)')

#7、xpath相对路径
>>> res=response.xpath('//a[contains(@href,"3")]')[0]
>>> res.xpath('img')
[<Selector xpath='img' data='<img src="image3_thumb.jpg">'>]
>>> res.xpath('./img')
[<Selector xpath='./img' data='<img src="image3_thumb.jpg">'>]
>>> res.xpath('.//img')
[<Selector xpath='.//img' data='<img src="image3_thumb.jpg">'>]
>>> res.xpath('//img') #这就是从头开始扫描
[<Selector xpath='//img' data='<img src="image1_thumb.jpg">'>, <Selector xpath='//img' data='<img src="image2_thumb.jpg">'>, <Selector xpath='//img' data='<img src="image3_thumb.jpg">'>, <Selector xpa
th='//img' data='<img src="image4_thumb.jpg">'>, <Selector xpath='//img' data='<img src="image5_thumb.jpg">'>]

#8、带变量的xpath
>>> response.xpath('//div[@id=$xxx]/a/text()',xxx='images').extract_first()
'Name: My image 1 '
>>> response.xpath('//div[count(a)=$yyy]/@id',yyy=5).extract_first() #求有5个a标签的div的id
'images'

选取器示例用法

 

澳门葡京备用网址 61澳门葡京备用网址 62

# -*- coding: utf-8 -*-
import scrapy
from scrapy.selector import HtmlXPathSelector
from scrapy.http.request import Request
from scrapy.http.cookies import CookieJar
from scrapy import FormRequest


class ChouTiSpider(scrapy.Spider):
    # 爬虫应用的名称,通过此名称启动爬虫命令
    name = "chouti"
    # 允许的域名
    allowed_domains = ["chouti.com"]

    cookie_dict = {}
    has_request_set = {}

    def start_requests(self):
        url = 'http://dig.chouti.com/'
        # return [Request(url=url, callback=self.login)]
        yield Request(url=url, callback=self.login)

    def login(self, response):
        cookie_jar = CookieJar()
        cookie_jar.extract_cookies(response, response.request)
        for k, v in cookie_jar._cookies.items():
            for i, j in v.items():
                for m, n in j.items():
                    self.cookie_dict[m] = n.value

        req = Request(
            url='http://dig.chouti.com/login',
            method='POST',
            headers={'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'},
            body='phone=8615131255089&password=pppppppp&oneMonth=1',
            cookies=self.cookie_dict,
            callback=self.check_login
        )
        yield req

    def check_login(self, response):
        req = Request(
            url='http://dig.chouti.com/',
            method='GET',
            callback=self.show,
            cookies=self.cookie_dict,
            dont_filter=True
        )
        yield req

    def show(self, response):
        # print(response)
        hxs = HtmlXPathSelector(response)
        news_list = hxs.select('//div[@id="content-list"]/div[@class="item"]')
        for new in news_list:
            # temp = new.xpath('div/div[@class="part2"]/@share-linkid').extract()
            link_id = new.xpath('*/div[@class="part2"]/@share-linkid').extract_first()
            yield Request(
                url='http://dig.chouti.com/link/vote?linksId=%s' %(link_id,),
                method='POST',
                cookies=self.cookie_dict,
                callback=self.do_favor
            )

        page_list = hxs.select('//div[@id="dig_lcpage"]//a[re:test(@href, "/all/hot/recent/\d+")]/@href').extract()
        for page in page_list:

            page_url = 'http://dig.chouti.com%s' % page
            import hashlib
            hash = hashlib.md5()
            hash.update(bytes(page_url,encoding='utf-8'))
            key = hash.hexdigest()
            if key in self.has_request_set:
                pass
            else:
                self.has_request_set[key] = page_url
                yield Request(
                    url=page_url,
                    method='GET',
                    callback=self.show
                )

    def do_favor(self, response):
        print(response.text)

以身作则:自动登陆抽屉并点赞

注意:settings.py中设置DEPTH_LIMIT =
1来指定“递归”的层数。

import requests
response=requests.get('http://dig.chouti.com/')
print(response.text)

View Code

5 Spiders

澳门葡京备用网址 63澳门葡京备用网址 64

#在项目目录下新建:entrypoint.py
from scrapy.cmdline import execute
execute(['scrapy', 'crawl', 'xiaohua'])

私下认可只幸亏cmd中实施爬虫,假设想在pycharm中实践供给做

强调:配置文件的选项必须是大写,如X=’1′

澳门葡京备用网址 65澳门葡京备用网址 66

# -*- coding: utf-8 -*-
import scrapy
from scrapy.linkextractors import LinkExtractor
from scrapy.spiders import CrawlSpider, Rule


class BaiduSpider(CrawlSpider):
    name = 'xiaohua'
    allowed_domains = ['www.xiaohuar.com']
    start_urls = ['http://www.xiaohuar.com/v/']
    # download_delay = 1

    rules = (
        Rule(LinkExtractor(allow=r'p\-\d\-\d+\.html$'), callback='parse_item',follow=True,),
    )


    def parse_item(self, response):

        if url:
            print('======下载视频==============================', url)
            yield scrapy.Request(url,callback=self.save)



    def save(self,response):
        print('======保存视频==============================',response.url,len(response.body))

        import time
        import hashlib
        m=hashlib.md5()
        m.update(str(time.time()).encode('utf-8'))
        m.update(response.url.encode('utf-8'))

        filename=r'E:\\mv\\%s.mp4' %m.hexdigest()
        with open(filename,'wb') as f:
            f.write(response.body)

模版:CrawlSpider

三. 格式化处理

上述实例只是不难的处理,所以在parse方法中从来处理。倘诺对于想要获取更多的多少处理,则足以应用Scrapy的items将数据格式化,然后统一交由pipelines来拍卖。

澳门葡京备用网址 67澳门葡京备用网址 68

import scrapy
from scrapy.selector import HtmlXPathSelector
from scrapy.http.request import Request
from scrapy.http.cookies import CookieJar
from scrapy import FormRequest


class XiaoHuarSpider(scrapy.Spider):
    # 爬虫应用的名称,通过此名称启动爬虫命令
    name = "xiaohuar"
    # 允许的域名
    allowed_domains = ["xiaohuar.com"]

    start_urls = [
        "http://www.xiaohuar.com/list-1-1.html",
    ]
    # custom_settings = {
    #     'ITEM_PIPELINES':{
    #         'spider1.pipelines.JsonPipeline': 100
    #     }
    # }
    has_request_set = {}

    def parse(self, response):
        # 分析页面
        # 找到页面中符合规则的内容(校花图片),保存
        # 找到所有的a标签,再访问其他a标签,一层一层的搞下去

        hxs = HtmlXPathSelector(response)

        items = hxs.select('//div[@class="item_list infinite_scroll"]/div')
        for item in items:
            src = item.select('.//div[@class="img"]/a/img/@src').extract_first()
            name = item.select('.//div[@class="img"]/span/text()').extract_first()
            school = item.select('.//div[@class="img"]/div[@class="btns"]/a/text()').extract_first()
            url = "http://www.xiaohuar.com%s" % src
            from ..items import XiaoHuarItem
            obj = XiaoHuarItem(name=name, school=school, url=url)
            yield obj

        urls = hxs.select('//a[re:test(@href, "http://www.xiaohuar.com/list-1-\d+.html")]/@href')
        for url in urls:
            key = self.md5(url)
            if key in self.has_request_set:
                pass
            else:
                self.has_request_set[key] = url
                req = Request(url=url,method='GET',callback=self.parse)
                yield req

    @staticmethod
    def md5(val):
        import hashlib
        ha = hashlib.md5()
        ha.update(bytes(val, encoding='utf-8'))
        key = ha.hexdigest()
        return key

spiders/xiahuar.py

澳门葡京备用网址 69澳门葡京备用网址 70

import scrapy


class XiaoHuarItem(scrapy.Item):
    name = scrapy.Field()
    school = scrapy.Field()
    url = scrapy.Field()

items

澳门葡京备用网址 71澳门葡京备用网址 72

import json
import os
import requests


class JsonPipeline(object):
    def __init__(self):
        self.file = open('xiaohua.txt', 'w')

    def process_item(self, item, spider):
        v = json.dumps(dict(item), ensure_ascii=False)
        self.file.write(v)
        self.file.write('\n')
        self.file.flush()
        return item


class FilePipeline(object):
    def __init__(self):
        if not os.path.exists('imgs'):
            os.makedirs('imgs')

    def process_item(self, item, spider):
        response = requests.get(item['url'], stream=True)
        file_name = '%s_%s.jpg' % (item['name'], item['school'])
        with open(os.path.join('imgs', file_name), mode='wb') as f:
            f.write(response.content)
        return item

pipelines

澳门葡京备用网址 73澳门葡京备用网址 74

ITEM_PIPELINES = {
   'spider1.pipelines.JsonPipeline': 100,
   'spider1.pipelines.FilePipeline': 300,
}
# 每行后面的整型值,确定了他们运行的顺序,item按数字从低到高的顺序,通过pipeline,通常将这些数字定义在0-1000范围内。

settings

对此pipeline可以做更加多,如下:

澳门葡京备用网址 75澳门葡京备用网址 76

from scrapy.exceptions import DropItem

class CustomPipeline(object):
    def __init__(self,v):
        self.value = v

    def process_item(self, item, spider):
        # 操作并进行持久化

        # return表示会被后续的pipeline继续处理
        return item

        # 表示将item丢弃,不会被后续pipeline处理
        # raise DropItem()


    @classmethod
    def from_crawler(cls, crawler):
        """
        初始化时候,用于创建pipeline对象
        :param crawler: 
        :return: 
        """
        val = crawler.settings.getint('MMMM')
        return cls(val)

    def open_spider(self,spider):
        """
        爬虫开始执行时,调用
        :param spider: 
        :return: 
        """
        print('000000')

    def close_spider(self,spider):
        """
        爬虫关闭时,被调用
        :param spider: 
        :return: 
        """
        print('111111')

自定义pipeline

四.中间件

澳门葡京备用网址 77澳门葡京备用网址 78

class SpiderMiddleware(object):

    def process_spider_input(self,response, spider):
        """
        下载完成,执行,然后交给parse处理
        :param response: 
        :param spider: 
        :return: 
        """
        pass

    def process_spider_output(self,response, result, spider):
        """
        spider处理完成,返回时调用
        :param response:
        :param result:
        :param spider:
        :return: 必须返回包含 Request 或 Item 对象的可迭代对象(iterable)
        """
        return result

    def process_spider_exception(self,response, exception, spider):
        """
        异常调用
        :param response:
        :param exception:
        :param spider:
        :return: None,继续交给后续中间件处理异常;含 Response 或 Item 的可迭代对象(iterable),交给调度器或pipeline
        """
        return None


    def process_start_requests(self,start_requests, spider):
        """
        爬虫启动时调用
        :param start_requests:
        :param spider:
        :return: 包含 Request 对象的可迭代对象
        """
        return start_requests

爬虫中间件

澳门葡京备用网址 79澳门葡京备用网址 80

class DownMiddleware1(object):
    def process_request(self, request, spider):
        """
        请求需要被下载时,经过所有下载器中间件的process_request调用
        :param request: 
        :param spider: 
        :return:  
            None,继续后续中间件去下载;
            Response对象,停止process_request的执行,开始执行process_response
            Request对象,停止中间件的执行,将Request重新调度器
            raise IgnoreRequest异常,停止process_request的执行,开始执行process_exception
        """
        pass



    def process_response(self, request, response, spider):
        """
        spider处理完成,返回时调用
        :param response:
        :param result:
        :param spider:
        :return: 
            Response 对象:转交给其他中间件process_response
            Request 对象:停止中间件,request会被重新调度下载
            raise IgnoreRequest 异常:调用Request.errback
        """
        print('response1')
        return response

    def process_exception(self, request, exception, spider):
        """
        当下载处理器(download handler)或 process_request() (下载中间件)抛出异常
        :param response:
        :param exception:
        :param spider:
        :return: 
            None:继续交给后续中间件处理异常;
            Response对象:停止后续process_exception方法
            Request对象:停止中间件,request将会被重新调用下载
        """
        return None

下载器中间件

五. 自定制命令

  • 在spiders同级创立任意目录,如:commands
  • 在里头创制 crawlall.py 文件 (此处文件名便是自定义的吩咐)
    澳门葡京备用网址 81澳门葡京备用网址 82

        from scrapy.commands import ScrapyCommand
        from scrapy.utils.project import get_project_settings
    
        class Command(ScrapyCommand):

            requires_project = True

            def syntax(self):
                return '[options]'

            def short_desc(self):
                return 'Runs all of the spiders'

            def run(self, args, opts):
                spider_list = self.crawler_process.spiders.list()
                for name in spider_list:
                    self.crawler_process.crawl(name, **opts.__dict__)
                self.crawler_process.start()

crawlall.py
  • 在settings.py 中丰硕配置 COMMANDS_MODULE = ‘项目名称.目录名称’
  • 在类型目录执行命令:scrapy crawlall 

六. 自定义扩张

自定义扩展时,利用信号在内定地方注册制定操作

澳门葡京备用网址 83澳门葡京备用网址 84

from scrapy import signals


class MyExtension(object):
    def __init__(self, value):
        self.value = value

    @classmethod
    def from_crawler(cls, crawler):
        val = crawler.settings.getint('MMMM')
        ext = cls(val)

        crawler.signals.connect(ext.spider_opened, signal=signals.spider_opened)
        crawler.signals.connect(ext.spider_closed, signal=signals.spider_closed)

        return ext

    def spider_opened(self, spider):
        print('open')

    def spider_closed(self, spider):
        print('close')

View Code

七. 幸免双重访问

scrapy私下认可使用 scrapy.dupefilter.路虎极光FPDupeFilter 举办去重,相关安顿有:

DUPEFILTER_CLASS = 'scrapy.dupefilter.RFPDupeFilter'
DUPEFILTER_DEBUG = False
JOBDIR = "保存范文记录的日志路径,如:/root/"  # 最终路径为 /root/requests.seen

澳门葡京备用网址 85澳门葡京备用网址 86

class RepeatUrl:
    def __init__(self):
        self.visited_url = set()

    @classmethod
    def from_settings(cls, settings):
        """
        初始化时,调用
        :param settings: 
        :return: 
        """
        return cls()

    def request_seen(self, request):
        """
        检测当前请求是否已经被访问过
        :param request: 
        :return: True表示已经访问过;False表示未访问过
        """
        if request.url in self.visited_url:
            return True
        self.visited_url.add(request.url)
        return False

    def open(self):
        """
        开始爬去请求时,调用
        :return: 
        """
        print('open replication')

    def close(self, reason):
        """
        结束爬虫爬取时,调用
        :param reason: 
        :return: 
        """
        print('close replication')

    def log(self, request, spider):
        """
        记录日志
        :param request: 
        :param spider: 
        :return: 
        """
        print('repeat', request.url)

自定义URAV4L去重操作

八.其他

澳门葡京备用网址 87澳门葡京备用网址 88

# -*- coding: utf-8 -*-

# Scrapy settings for step8_king project
#
# For simplicity, this file contains only settings considered important or
# commonly used. You can find more settings consulting the documentation:
#
#     http://doc.scrapy.org/en/latest/topics/settings.html
#     http://scrapy.readthedocs.org/en/latest/topics/downloader-middleware.html
#     http://scrapy.readthedocs.org/en/latest/topics/spider-middleware.html

# 1. 爬虫名称
BOT_NAME = 'step8_king'

# 2. 爬虫应用路径
SPIDER_MODULES = ['step8_king.spiders']
NEWSPIDER_MODULE = 'step8_king.spiders'

# Crawl responsibly by identifying yourself (and your website) on the user-agent
# 3. 客户端 user-agent请求头
# USER_AGENT = 'step8_king (+http://www.yourdomain.com)'

# Obey robots.txt rules
# 4. 禁止爬虫配置
# ROBOTSTXT_OBEY = False

# Configure maximum concurrent requests performed by Scrapy (default: 16)
# 5. 并发请求数
# CONCURRENT_REQUESTS = 4

# Configure a delay for requests for the same website (default: 0)
# See http://scrapy.readthedocs.org/en/latest/topics/settings.html#download-delay
# See also autothrottle settings and docs
# 6. 延迟下载秒数
# DOWNLOAD_DELAY = 2


# The download delay setting will honor only one of:
# 7. 单域名访问并发数,并且延迟下次秒数也应用在每个域名
# CONCURRENT_REQUESTS_PER_DOMAIN = 2
# 单IP访问并发数,如果有值则忽略:CONCURRENT_REQUESTS_PER_DOMAIN,并且延迟下次秒数也应用在每个IP
# CONCURRENT_REQUESTS_PER_IP = 3

# Disable cookies (enabled by default)
# 8. 是否支持cookie,cookiejar进行操作cookie
# COOKIES_ENABLED = True
# COOKIES_DEBUG = True

# Disable Telnet Console (enabled by default)
# 9. Telnet用于查看当前爬虫的信息,操作爬虫等...
#    使用telnet ip port ,然后通过命令操作
# TELNETCONSOLE_ENABLED = True
# TELNETCONSOLE_HOST = '127.0.0.1'
# TELNETCONSOLE_PORT = [6023,]


# 10. 默认请求头
# Override the default request headers:
# DEFAULT_REQUEST_HEADERS = {
#     'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
#     'Accept-Language': 'en',
# }


# Configure item pipelines
# See http://scrapy.readthedocs.org/en/latest/topics/item-pipeline.html
# 11. 定义pipeline处理请求
# ITEM_PIPELINES = {
#    'step8_king.pipelines.JsonPipeline': 700,
#    'step8_king.pipelines.FilePipeline': 500,
# }



# 12. 自定义扩展,基于信号进行调用
# Enable or disable extensions
# See http://scrapy.readthedocs.org/en/latest/topics/extensions.html
# EXTENSIONS = {
#     # 'step8_king.extensions.MyExtension': 500,
# }


# 13. 爬虫允许的最大深度,可以通过meta查看当前深度;0表示无深度
# DEPTH_LIMIT = 3

# 14. 爬取时,0表示深度优先Lifo(默认);1表示广度优先FiFo

# 后进先出,深度优先
# DEPTH_PRIORITY = 0
# SCHEDULER_DISK_QUEUE = 'scrapy.squeue.PickleLifoDiskQueue'
# SCHEDULER_MEMORY_QUEUE = 'scrapy.squeue.LifoMemoryQueue'
# 先进先出,广度优先

# DEPTH_PRIORITY = 1
# SCHEDULER_DISK_QUEUE = 'scrapy.squeue.PickleFifoDiskQueue'
# SCHEDULER_MEMORY_QUEUE = 'scrapy.squeue.FifoMemoryQueue'

# 15. 调度器队列
# SCHEDULER = 'scrapy.core.scheduler.Scheduler'
# from scrapy.core.scheduler import Scheduler


# 16. 访问URL去重
# DUPEFILTER_CLASS = 'step8_king.duplication.RepeatUrl'


# Enable and configure the AutoThrottle extension (disabled by default)
# See http://doc.scrapy.org/en/latest/topics/autothrottle.html

"""
17. 自动限速算法
    from scrapy.contrib.throttle import AutoThrottle
    自动限速设置
    1. 获取最小延迟 DOWNLOAD_DELAY
    2. 获取最大延迟 AUTOTHROTTLE_MAX_DELAY
    3. 设置初始下载延迟 AUTOTHROTTLE_START_DELAY
    4. 当请求下载完成后,获取其"连接"时间 latency,即:请求连接到接受到响应头之间的时间
    5. 用于计算的... AUTOTHROTTLE_TARGET_CONCURRENCY
    target_delay = latency / self.target_concurrency
    new_delay = (slot.delay + target_delay) / 2.0 # 表示上一次的延迟时间
    new_delay = max(target_delay, new_delay)
    new_delay = min(max(self.mindelay, new_delay), self.maxdelay)
    slot.delay = new_delay
"""

# 开始自动限速
# 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 = 10
# 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 = True

# Enable and configure HTTP caching (disabled by default)
# See http://scrapy.readthedocs.org/en/latest/topics/downloader-middleware.html#httpcache-middleware-settings


"""
18. 启用缓存
    目的用于将已经发送的请求或相应缓存下来,以便以后使用

    from scrapy.downloadermiddlewares.httpcache import HttpCacheMiddleware
    from scrapy.extensions.httpcache import DummyPolicy
    from scrapy.extensions.httpcache import FilesystemCacheStorage
"""
# 是否启用缓存策略
# HTTPCACHE_ENABLED = True

# 缓存策略:所有请求均缓存,下次在请求直接访问原来的缓存即可
# HTTPCACHE_POLICY = "scrapy.extensions.httpcache.DummyPolicy"
# 缓存策略:根据Http响应头:Cache-Control、Last-Modified 等进行缓存的策略
# HTTPCACHE_POLICY = "scrapy.extensions.httpcache.RFC2616Policy"

# 缓存超时时间
# HTTPCACHE_EXPIRATION_SECS = 0

# 缓存保存路径
# HTTPCACHE_DIR = 'httpcache'

# 缓存忽略的Http状态码
# HTTPCACHE_IGNORE_HTTP_CODES = []

# 缓存存储的插件
# HTTPCACHE_STORAGE = 'scrapy.extensions.httpcache.FilesystemCacheStorage'


"""
19. 代理,需要在环境变量中设置
    from scrapy.contrib.downloadermiddleware.httpproxy import HttpProxyMiddleware

    方式一:使用默认
        os.environ
        {
            http_proxy:http://root:woshiniba@192.168.11.11:9999/
            https_proxy:http://192.168.11.11:9999/
        }
    方式二:使用自定义下载中间件

    def to_bytes(text, encoding=None, errors='strict'):
        if isinstance(text, bytes):
            return text
        if not isinstance(text, six.string_types):
            raise TypeError('to_bytes must receive a unicode, str or bytes '
                            'object, got %s' % type(text).__name__)
        if encoding is None:
            encoding = 'utf-8'
        return text.encode(encoding, errors)

    class ProxyMiddleware(object):
        def process_request(self, request, spider):
            PROXIES = [
                {'ip_port': '111.11.228.75:80', 'user_pass': ''},
                {'ip_port': '120.198.243.22:80', 'user_pass': ''},
                {'ip_port': '111.8.60.9:8123', 'user_pass': ''},
                {'ip_port': '101.71.27.120:80', 'user_pass': ''},
                {'ip_port': '122.96.59.104:80', 'user_pass': ''},
                {'ip_port': '122.224.249.122:8088', 'user_pass': ''},
            ]
            proxy = random.choice(PROXIES)
            if proxy['user_pass'] is not None:
                request.meta['proxy'] = to_bytes("http://%s" % proxy['ip_port'])
                encoded_user_pass = base64.encodestring(to_bytes(proxy['user_pass']))
                request.headers['Proxy-Authorization'] = to_bytes('Basic ' + encoded_user_pass)
                print "**************ProxyMiddleware have pass************" + proxy['ip_port']
            else:
                print "**************ProxyMiddleware no pass************" + proxy['ip_port']
                request.meta['proxy'] = to_bytes("http://%s" % proxy['ip_port'])

    DOWNLOADER_MIDDLEWARES = {
       'step8_king.middlewares.ProxyMiddleware': 500,
    }

"""

"""
20. Https访问
    Https访问时有两种情况:
    1. 要爬取网站使用的可信任证书(默认支持)
        DOWNLOADER_HTTPCLIENTFACTORY = "scrapy.core.downloader.webclient.ScrapyHTTPClientFactory"
        DOWNLOADER_CLIENTCONTEXTFACTORY = "scrapy.core.downloader.contextfactory.ScrapyClientContextFactory"

    2. 要爬取网站使用的自定义证书
        DOWNLOADER_HTTPCLIENTFACTORY = "scrapy.core.downloader.webclient.ScrapyHTTPClientFactory"
        DOWNLOADER_CLIENTCONTEXTFACTORY = "step8_king.https.MySSLFactory"

        # https.py
        from scrapy.core.downloader.contextfactory import ScrapyClientContextFactory
        from twisted.internet.ssl import (optionsForClientTLS, CertificateOptions, PrivateCertificate)

        class MySSLFactory(ScrapyClientContextFactory):
            def getCertificateOptions(self):
                from OpenSSL import crypto
                v1 = crypto.load_privatekey(crypto.FILETYPE_PEM, open('/Users/wupeiqi/client.key.unsecure', mode='r').read())
                v2 = crypto.load_certificate(crypto.FILETYPE_PEM, open('/Users/wupeiqi/client.pem', mode='r').read())
                return CertificateOptions(
                    privateKey=v1,  # pKey对象
                    certificate=v2,  # X509对象
                    verify=False,
                    method=getattr(self, 'method', getattr(self, '_ssl_method', None))
                )
    其他:
        相关类
            scrapy.core.downloader.handlers.http.HttpDownloadHandler
            scrapy.core.downloader.webclient.ScrapyHTTPClientFactory
            scrapy.core.downloader.contextfactory.ScrapyClientContextFactory
        相关配置
            DOWNLOADER_HTTPCLIENTFACTORY
            DOWNLOADER_CLIENTCONTEXTFACTORY

"""



"""
21. 爬虫中间件
    class SpiderMiddleware(object):

        def process_spider_input(self,response, spider):
            '''
            下载完成,执行,然后交给parse处理
            :param response: 
            :param spider: 
            :return: 
            '''
            pass

        def process_spider_output(self,response, result, spider):
            '''
            spider处理完成,返回时调用
            :param response:
            :param result:
            :param spider:
            :return: 必须返回包含 Request 或 Item 对象的可迭代对象(iterable)
            '''
            return result

        def process_spider_exception(self,response, exception, spider):
            '''
            异常调用
            :param response:
            :param exception:
            :param spider:
            :return: None,继续交给后续中间件处理异常;含 Response 或 Item 的可迭代对象(iterable),交给调度器或pipeline
            '''
            return None


        def process_start_requests(self,start_requests, spider):
            '''
            爬虫启动时调用
            :param start_requests:
            :param spider:
            :return: 包含 Request 对象的可迭代对象
            '''
            return start_requests

    内置爬虫中间件:
        'scrapy.contrib.spidermiddleware.httperror.HttpErrorMiddleware': 50,
        'scrapy.contrib.spidermiddleware.offsite.OffsiteMiddleware': 500,
        'scrapy.contrib.spidermiddleware.referer.RefererMiddleware': 700,
        'scrapy.contrib.spidermiddleware.urllength.UrlLengthMiddleware': 800,
        'scrapy.contrib.spidermiddleware.depth.DepthMiddleware': 900,

"""
# from scrapy.contrib.spidermiddleware.referer import RefererMiddleware
# Enable or disable spider middlewares
# See http://scrapy.readthedocs.org/en/latest/topics/spider-middleware.html
SPIDER_MIDDLEWARES = {
   # 'step8_king.middlewares.SpiderMiddleware': 543,
}


"""
22. 下载中间件
    class DownMiddleware1(object):
        def process_request(self, request, spider):
            '''
            请求需要被下载时,经过所有下载器中间件的process_request调用
            :param request:
            :param spider:
            :return:
                None,继续后续中间件去下载;
                Response对象,停止process_request的执行,开始执行process_response
                Request对象,停止中间件的执行,将Request重新调度器
                raise IgnoreRequest异常,停止process_request的执行,开始执行process_exception
            '''
            pass



        def process_response(self, request, response, spider):
            '''
            spider处理完成,返回时调用
            :param response:
            :param result:
            :param spider:
            :return:
                Response 对象:转交给其他中间件process_response
                Request 对象:停止中间件,request会被重新调度下载
                raise IgnoreRequest 异常:调用Request.errback
            '''
            print('response1')
            return response

        def process_exception(self, request, exception, spider):
            '''
            当下载处理器(download handler)或 process_request() (下载中间件)抛出异常
            :param response:
            :param exception:
            :param spider:
            :return:
                None:继续交给后续中间件处理异常;
                Response对象:停止后续process_exception方法
                Request对象:停止中间件,request将会被重新调用下载
            '''
            return None


    默认下载中间件
    {
        'scrapy.contrib.downloadermiddleware.robotstxt.RobotsTxtMiddleware': 100,
        'scrapy.contrib.downloadermiddleware.httpauth.HttpAuthMiddleware': 300,
        'scrapy.contrib.downloadermiddleware.downloadtimeout.DownloadTimeoutMiddleware': 350,
        'scrapy.contrib.downloadermiddleware.useragent.UserAgentMiddleware': 400,
        'scrapy.contrib.downloadermiddleware.retry.RetryMiddleware': 500,
        'scrapy.contrib.downloadermiddleware.defaultheaders.DefaultHeadersMiddleware': 550,
        'scrapy.contrib.downloadermiddleware.redirect.MetaRefreshMiddleware': 580,
        'scrapy.contrib.downloadermiddleware.httpcompression.HttpCompressionMiddleware': 590,
        'scrapy.contrib.downloadermiddleware.redirect.RedirectMiddleware': 600,
        'scrapy.contrib.downloadermiddleware.cookies.CookiesMiddleware': 700,
        'scrapy.contrib.downloadermiddleware.httpproxy.HttpProxyMiddleware': 750,
        'scrapy.contrib.downloadermiddleware.chunked.ChunkedTransferMiddleware': 830,
        'scrapy.contrib.downloadermiddleware.stats.DownloaderStats': 850,
        'scrapy.contrib.downloadermiddleware.httpcache.HttpCacheMiddleware': 900,
    }

"""
# from scrapy.contrib.downloadermiddleware.httpauth import HttpAuthMiddleware
# Enable or disable downloader middlewares
# See http://scrapy.readthedocs.org/en/latest/topics/downloader-middleware.html
# DOWNLOADER_MIDDLEWARES = {
#    'step8_king.middlewares.DownMiddleware1': 100,
#    'step8_king.middlewares.DownMiddleware2': 500,
# }

settings 

九.TinyScrapy

澳门葡京备用网址 89澳门葡京备用网址 90

#!/usr/bin/env python
# -*- coding:utf-8 -*-
import types
from twisted.internet import defer
from twisted.web.client import getPage
from twisted.internet import reactor



class Request(object):
    def __init__(self, url, callback):
        self.url = url
        self.callback = callback
        self.priority = 0


class HttpResponse(object):
    def __init__(self, content, request):
        self.content = content
        self.request = request


class ChouTiSpider(object):

    def start_requests(self):
        url_list = ['http://www.cnblogs.com/', 'http://www.bing.com']
        for url in url_list:
            yield Request(url=url, callback=self.parse)

    def parse(self, response):
        print(response.request.url)
        # yield Request(url="http://www.baidu.com", callback=self.parse)




from queue import Queue
Q = Queue()


class CallLaterOnce(object):
    def __init__(self, func, *a, **kw):
        self._func = func
        self._a = a
        self._kw = kw
        self._call = None

    def schedule(self, delay=0):
        if self._call is None:
            self._call = reactor.callLater(delay, self)

    def cancel(self):
        if self._call:
            self._call.cancel()

    def __call__(self):
        self._call = None
        return self._func(*self._a, **self._kw)


class Engine(object):
    def __init__(self):
        self.nextcall = None
        self.crawlling = []
        self.max = 5
        self._closewait = None

    def get_response(self,content, request):
        response = HttpResponse(content, request)
        gen = request.callback(response)
        if isinstance(gen, types.GeneratorType):
            for req in gen:
                req.priority = request.priority + 1
                Q.put(req)


    def rm_crawlling(self,response,d):
        self.crawlling.remove(d)

    def _next_request(self,spider):
        if Q.qsize() == 0 and len(self.crawlling) == 0:
            self._closewait.callback(None)

        if len(self.crawlling) >= 5:
            return
        while len(self.crawlling) < 5:
            try:
                req = Q.get(block=False)
            except Exception as e:
                req = None
            if not req:
                return
            d = getPage(req.url.encode('utf-8'))
            self.crawlling.append(d)
            d.addCallback(self.get_response, req)
            d.addCallback(self.rm_crawlling,d)
            d.addCallback(lambda _: self.nextcall.schedule())


    @defer.inlineCallbacks
    def crawl(self):
        spider = ChouTiSpider()
        start_requests = iter(spider.start_requests())
        flag = True
        while flag:
            try:
                req = next(start_requests)
                Q.put(req)
            except StopIteration as e:
                flag = False

        self.nextcall = CallLaterOnce(self._next_request,spider)
        self.nextcall.schedule()

        self._closewait = defer.Deferred()
        yield self._closewait

    @defer.inlineCallbacks
    def pp(self):
        yield self.crawl()

_active = set()
obj = Engine()
d = obj.crawl()
_active.add(d)

li = defer.DeferredList(_active)
li.addBoth(lambda _,*a,**kw: reactor.stop())

reactor.run()

参考版

点击下载

 越来越多文书档案参见:

 

    b、带参数get请求—–》》params

2- 多进程经过回调函数处理

十 、 爬取亚马逊商品音信

澳门葡京备用网址 91澳门葡京备用网址 92

1、
scrapy startproject Amazon
cd Amazon
scrapy genspider spider_goods www.amazon.cn

2、settings.py
ROBOTSTXT_OBEY = False
#请求头
DEFAULT_REQUEST_HEADERS = {
    'Referer':'https://www.amazon.cn/',
    'User-Agent':'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.75 Safari/537.36'
}
#打开注释
HTTPCACHE_ENABLED = True
HTTPCACHE_EXPIRATION_SECS = 0
HTTPCACHE_DIR = 'httpcache'
HTTPCACHE_IGNORE_HTTP_CODES = []
HTTPCACHE_STORAGE = 'scrapy.extensions.httpcache.FilesystemCacheStorage'

3、items.py
class GoodsItem(scrapy.Item):
    # define the fields for your item here like:
    # name = scrapy.Field()
    #商品名字
    goods_name = scrapy.Field()
    #价钱
    goods_price = scrapy.Field()
    #配送方式
    delivery_method=scrapy.Field()

4、spider_goods.py
# -*- coding: utf-8 -*-
import scrapy

from Amazon.items import  GoodsItem
from scrapy.http import Request
from urllib.parse import urlencode

class SpiderGoodsSpider(scrapy.Spider):
    name = 'spider_goods'
    allowed_domains = ['www.amazon.cn']
    # start_urls = ['http://www.amazon.cn/']


    def __int__(self,keyword=None,*args,**kwargs):
        super(SpiderGoodsSpider).__init__(*args,**kwargs)
        self.keyword=keyword

    def start_requests(self):
        url='https://www.amazon.cn/s/ref=nb_sb_noss_1?'
        paramas={
            '__mk_zh_CN': '亚马逊网站',
            'url': 'search - alias = aps',
            'field-keywords': self.keyword
        }
        url=url+urlencode(paramas,encoding='utf-8')
        yield Request(url,callback=self.parse_index)


    def parse_index(self, response):
        print('解析索引页:%s' %response.url)

        urls=response.xpath('//*[contains(@id,"result_")]/div/div[3]/div[1]/a/@href').extract()
        for url in urls:
            yield Request(url,callback=self.parse_detail)

        next_url=response.urljoin(response.xpath('//*[@id="pagnNextLink"]/@href').extract_first())
        print('下一页的url',next_url)
        yield Request(next_url,callback=self.parse_index)

    def parse_detail(self,response):
        print('解析详情页:%s' %(response.url))

        item=GoodsItem()
        # 商品名字
        item['goods_name'] = response.xpath('//*[@id="productTitle"]/text()').extract_first().strip()
        # 价钱
        item['goods_price'] = response.xpath('//*[@id="priceblock_ourprice"]/text()').extract_first().strip()
        # 配送方式
        item['delivery_method'] = ''.join(response.xpath('//*[@id="ddmMerchantMessage"]//text()').extract())
        return item

5、自定义pipelines
#sql.py
import pymysql
import settings


MYSQL_HOST=settings.MYSQL_HOST
MYSQL_PORT=settings.MYSQL_PORT
MYSQL_USER=settings.MYSQL_USER
MYSQL_PWD=settings.MYSQL_PWD
MYSQL_DB=settings.MYSQL_DB

conn=pymysql.connect(
    host=MYSQL_HOST,
    port=int(MYSQL_PORT),
    user=MYSQL_USER,
    password=MYSQL_PWD,
    db=MYSQL_DB,
    charset='utf8'
)
cursor=conn.cursor()

class Mysql(object):
    @staticmethod
    def insert_tables_goods(goods_name,goods_price,deliver_mode):
        sql='insert into goods(goods_name,goods_price,delivery_method) values(%s,%s,%s)'
        cursor.execute(sql,args=(goods_name,goods_price,deliver_mode))
        conn.commit()

    @staticmethod
    def is_repeat(goods_name):
        sql='select count(1) from goods where goods_name=%s'
        cursor.execute(sql,args=(goods_name,))
        if cursor.fetchone()[0] >= 1:
            return True

if __name__ == '__main__':
    cursor.execute('select * from goods;')
    print(cursor.fetchall())


#pipelines.py
from Amazon.mysqlpipelines.sql import Mysql


class AmazonPipeline(object):
    def process_item(self, item, spider):
        goods_name=item['goods_name']
        goods_price=item['goods_price']
        delivery_mode=item['delivery_method']
        if not Mysql.is_repeat(goods_name):
            Mysql.insert_table_goods(goods_name,goods_price,delivery_mode)



6、创建数据库表
create database amazon charset utf8;
create table goods(
    id int primary key auto_increment,
    goods_name char(30),
    goods_price char(20),
    delivery_method varchar(50)
);

7、settings.py
MYSQL_HOST='localhost'
MYSQL_PORT='3306'
MYSQL_USER='root'
MYSQL_PWD='123'
MYSQL_DB='amazon'


#数字代表优先级程度(1-1000随意设置,数值越低,组件的优先级越高)
ITEM_PIPELINES = {
   'Amazon.mysqlpipelines.pipelines.mazonPipeline': 1,
}


#8、在项目目录下新建:entrypoint.py
from scrapy.cmdline import execute
execute(['scrapy', 'crawl', 'spider_goods','-a','keyword=iphone8'])

View Code

 

 

 

 

    c、带参数get请求—–》》headers

澳门葡京备用网址 93澳门葡京备用网址 94

#通常我们在发送请求时都需要带上请求头,请求头是将自身伪装成浏览器的关键,常见的有用的请求头如下
Host
Referer #大型网站通常都会根据该参数判断请求的来源
User-Agent #客户端
Cookie #Cookie信息虽然包含在请求头里,但requests模块有单独的参数来处理他,headers={}内就不要放它了
 1 from concurrent.futures import ProcessPoolExecutor
 2 import requests
 3 import time
 4 
 5 def task(url):
 6     response = requests.get(url)
 7     return response
 8 
 9 def done(future,*args,**kwargs):
10     response = future.result()
11     print(response.status_code,response.content)
12 
13 pool = ProcessPoolExecutor(7)
14 url_list = [
15     'http://www.cnblogs.com/wupeiqi',
16     'http://huaban.com/favorite/beauty/',
17     'http://www.bing.com',
18     'http://www.zhihu.com',
19     'http://www.sina.com',
20     'http://www.baidu.com',
21     'http://www.autohome.com.cn',
22 ]
23 for url in url_list:
24     v = pool.submit(task,url)
25     v.add_done_callback(done)
26 
27 pool.shutdown(wait=True)

     d、带参数get请求—–》》cookies

View Code

三、基于post请求

二 八线程执行

     a、介绍

爬虫能够完成产出,然而,请求发送出去后和重临在此以前,中间时代线程空闲。

#GET请求
HTTP默认的请求方法就是GET
     * 没有请求体
     * 数据必须在1K之内!
     * GET请求数据会暴露在浏览器的地址栏中

GET请求常用的操作:
       1. 在浏览器的地址栏中直接给出URL,那么就一定是GET请求
       2. 点击页面上的超链接也一定是GET请求
       3. 提交表单时,表单默认使用GET请求,但可以设置为POST


#POST请求
(1). 数据不会出现在地址栏中
(2). 数据的大小没有上限
(3). 有请求体
(4). 请求体中如果存在中文,会使用URL编码!


#!!!requests.post()用法与requests.get()完全一致,特殊的是requests.post()有一个data参数,用来存放请求体数据
复制代码

编排情势:
1 十六线程直接回四处理

    b、发送POST的呼吁,模拟浏览器的报到行为

from concurrent.futures import ThreadPoolExecutor
import requests
import time

def task(url):
    response = requests.get(url)
    print(url,response)
    # 写正则表达式

pool = ThreadPoolExecutor(7)
url_list = [
    'http://www.cnblogs.com/wupeiqi',
    'http://huaban.com/favorite/beauty/',
    'http://www.bing.com',
    'http://www.zhihu.com',
    'http://www.sina.com',
    'http://www.baidu.com',
    'http://www.autohome.com.cn',
]
for url in url_list:
    pool.submit(task,url)

pool.shutdown(wait=True)

四、响应Response

2 三十二线程通过回调函数处理

    a、response属性

from concurrent.futures import ThreadPoolExecutor
import requests
import time

def task(url):
    """
    下载页面
    :param url:
    :return:
    """
    response = requests.get(url)
    return response

def done(future,*args,**kwargs):
    response = future.result()
    print(response.status_code,response.content)

pool = ThreadPoolExecutor(7)
url_list = [
    'http://www.cnblogs.com/wupeiqi',
    'http://huaban.com/favorite/beauty/',
    'http://www.bing.com',
    'http://www.zhihu.com',
    'http://www.sina.com',
    'http://www.baidu.com',
    'http://www.autohome.com.cn',
]
for url in url_list:
    v = pool.submit(task,url)
    v.add_done_callback(done)

pool.shutdown(wait=True)

澳门葡京备用网址 95澳门葡京备用网址 96

透过上述代码均能够做到对请求品质的增高,对于多线程和多实行的老毛病是在IO阻塞时会造成了线程和进度的荒废

import requests
respone=requests.get('http://www.jianshu.com')
# respone属性
print(respone.text)
print(respone.content)

print(respone.status_code)
print(respone.headers)
print(respone.cookies)
print(respone.cookies.get_dict())
print(respone.cookies.items())

print(respone.url)
print(respone.history)

print(respone.encoding)

#关闭:response.close()
from contextlib import closing
with closing(requests.get('xxx',stream=True)) as response:
    for line in response.iter_content():
    pass

三 异步非阻塞模块

View Code

协程(微线程) + 异步IO—>
二个线程发送N个Http请求

    b、编码难点

澳门葡京备用网址 97澳门葡京备用网址 98

#编码问题
import requests
response=requests.get('http://www.autohome.com/news')
# response.encoding='gbk' #汽车之家网站返回的页面内容为gb2312编码的,而requests的默认编码为ISO-8859-1,如果不设置成gbk则中文乱码
print(response.text)
import asyncio
@asyncio.coroutine
def task():
    print('before...task......')
    yield from asyncio.sleep(5) # 发送Http请求,支持TCP获取结果..
    print('end...task......')


tasks = [task(), task()]

loop = asyncio.get_event_loop()
loop.run_until_complete(asyncio.gather(*tasks))
loop.close()

    c、获取二进制

one

#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)

澳门葡京备用网址 99澳门葡京备用网址 100

    d、解析json

 1 import asyncio
 2 @asyncio.coroutine
 3 def task(host, url='/'):
 4     print('start',host,url)
 5     reader, writer = yield from asyncio.open_connection(host, 80)
 6 
 7     request_header_content = "GET %s HTTP/1.0\r\nHost: %s\r\n\r\n" % (url, host,)
 8     request_header_content = bytes(request_header_content, encoding='utf-8')
 9 
10     writer.write(request_header_content)
11     yield from writer.drain()
12     text = yield from reader.read()
13     print('end',host, url, text)
14     writer.close()
15 
16 tasks = [
17     task('www.cnblogs.com', '/gregoryli/'),
18     task('dig.chouti.com', '/pic/show?nid=4073644713430508&lid=10273091')
19 ]
20 
21 loop = asyncio.get_event_loop()
22 results = loop.run_until_complete(asyncio.gather(*tasks))
23 loop.close()
#解析json
import requests
response=requests.get('http://httpbin.org/get')

import json
res1=json.loads(response.text) #太麻烦

res2=response.json() #直接获取json数据


print(res1 == res2) #True

two

五、selenium模块

澳门葡京备用网址 101澳门葡京备用网址 102

    a、介绍

import aiohttp
import asyncio
@asyncio.coroutine
def fetch_async(url):
    print(url)
    response = yield from aiohttp.request('GET', url)
    print(url, response)
    response.close()

tasks = [fetch_async('http://www.baidu.com/'), fetch_async('http://www.chouti.com/')]

event_loop = asyncio.get_event_loop()
results = event_loop.run_until_complete(asyncio.gather(*tasks))
event_loop.close()
selenium最初是一个自动化测试工具,而爬虫中使用它主要是为了解决requests无法直接执行JavaScript代码的问题

selenium本质是通过驱动浏览器,完全模拟浏览器的操作,比如跳转、输入、点击、下拉等,来拿到网页渲染之后的结果,可支持多种浏览器

from selenium import webdriver
browser=webdriver.Chrome()
browser=webdriver.Firefox()
browser=webdriver.PhantomJS()
browser=webdriver.Safari()
browser=webdriver.Edge()

aiohttp

   b、安装

澳门葡京备用网址 103澳门葡京备用网址 104

#安装:selenium+chromedriver
pip3 install selenium
下载chromdriver.exe放到python安装路径的scripts目录中即可,注意最新版本是2.29,并非2.9
国内镜像网站地址:http://npm.taobao.org/mirrors/chromedriver/2.29/
最新的版本去官网找:https://sites.google.com/a/chromium.org/chromedriver/downloads

#注意:
selenium3默认支持的webdriver是Firfox,而Firefox需要安装geckodriver
下载链接:https://github.com/mozilla/geckodriver/releases
 1 # -*- coding: utf-8 -*-
 2 # 2017/11/17 14:04
 3 import asyncio
 4 import requests
 5 
 6 @asyncio.coroutine
 7 def task(func, *args):
 8     print(func,args)
 9     loop = asyncio.get_event_loop()
10     future = loop.run_in_executor(None, func, *args) # requests.get('http://www.cnblogs.com/wupeiqi/')
11     response = yield from future
12     print(response.url, response.content)
13 
14 tasks = [
15     task(requests.get, 'http://www.cnblogs.com/gregoryli/'),
16     task(requests.get, 'http://dig.chouti.com/pic/show?nid=4073644713430508&lid=10273091')
17 ]
18 
19 loop = asyncio.get_event_loop()
20 results = loop.run_until_complete(asyncio.gather(*tasks))
21 loop.close()

六、选择器

requests

   a、基本采取

澳门葡京备用网址 105澳门葡京备用网址 106

澳门葡京备用网址 107澳门葡京备用网址 108

import gevent
import requests
from gevent import monkey

monkey.patch_all()

def task(method, url, req_kwargs):
    print(method, url, req_kwargs)
    response = requests.request(method=method, url=url, **req_kwargs)
    print(response.url, response.content)

# ##### 发送请求 #####
# gevent.joinall([
#     gevent.spawn(task, method='get', url='https://www.python.org/', req_kwargs={}),
#     gevent.spawn(task, method='get', url='https://www.yahoo.com/', req_kwargs={}),
#     gevent.spawn(task, method='get', url='https://github.com/', req_kwargs={}),
# ])

# ##### 发送请求(协程池控制最大协程数量) #####
from gevent.pool import Pool
pool = Pool(5)
gevent.joinall([
    pool.spawn(task, method='get', url='https://www.python.org/', req_kwargs={}),
    pool.spawn(task, method='get', url='https://www.yahoo.com/', req_kwargs={}),
    pool.spawn(task, method='get', url='https://www.github.com/', req_kwargs={}),
])
from selenium import webdriver
from selenium.webdriver import ActionChains
from selenium.webdriver.common.by import By #按照什么方式查找,By.ID,By.CSS_SELECTOR
from selenium.webdriver.common.keys import Keys #键盘按键操作
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.wait import WebDriverWait #等待页面加载某些元素
import time

driver=webdriver.Chrome()
driver.get('https://www.baidu.com')
wait=WebDriverWait(driver,10)

try:
    #===============所有方法===================
    # 1、find_element_by_id
    # 2、find_element_by_link_text
    # 3、find_element_by_partial_link_text
    # 4、find_element_by_tag_name
    # 5、find_element_by_class_name
    # 6、find_element_by_name
    # 7、find_element_by_css_selector
    # 8、find_element_by_xpath
    # 强调:
    # 1、上述均可以改写成find_element(By.ID,'kw')的形式
    # 2、find_elements_by_xxx的形式是查找到多个元素,结果为列表

    #===============示范用法===================
    # 1、find_element_by_id
    print(driver.find_element_by_id('kw'))

    # 2、find_element_by_link_text
    # login=driver.find_element_by_link_text('登录')
    # login.click()

    # 3、find_element_by_partial_link_text
    login=driver.find_elements_by_partial_link_text('录')[0]
    login.click()

    # 4、find_element_by_tag_name
    print(driver.find_element_by_tag_name('a'))

    # 5、find_element_by_class_name
    button=wait.until(EC.element_to_be_clickable((By.CLASS_NAME,'tang-pass-footerBarULogin')))
    button.click()

    # 6、find_element_by_name
    input_user=wait.until(EC.presence_of_element_located((By.NAME,'userName')))
    input_pwd=wait.until(EC.presence_of_element_located((By.NAME,'password')))
    commit=wait.until(EC.element_to_be_clickable((By.ID,'TANGRAM__PSP_10__submit')))

    input_user.send_keys('18611453110')
    input_pwd.send_keys('lhf@094573')
    commit.click()

    # 7、find_element_by_css_selector
    driver.find_element_by_css_selector('#kw')

    # 8、find_element_by_xpath

    time.sleep(5)

finally:
    driver.close()

gevent+requests

View Code

澳门葡京备用网址 109澳门葡京备用网址 110

   b、xpath

 1 import grequests
 2 
 3 request_list = [
 4     grequests.get('http://httpbin.org/delay/1', timeout=0.001),
 5     grequests.get('http://fakedomain/'),
 6     grequests.get('http://httpbin.org/status/500')
 7 ]
 8 
 9 # ##### 执行并获取响应列表 #####
10 response_list = grequests.map(request_list,size=5)
11 print(response_list)

   c、获取标签属性

grequests

from selenium import webdriver
from selenium.webdriver import ActionChains
from selenium.webdriver.common.by import By #按照什么方式查找,By.ID,By.CSS_SELECTOR
from selenium.webdriver.common.keys import Keys #键盘按键操作
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.wait import WebDriverWait #等待页面加载某些元素

browser=webdriver.Chrome()

browser.get('https://www.amazon.cn/')

wait=WebDriverWait(browser,10)
wait.until(EC.presence_of_element_located((By.ID,'cc-lm-tcgShowImgContainer')))

tag=browser.find_element(By.CSS_SELECTOR,'#cc-lm-tcgShowImgContainer img')

#获取标签属性,
print(tag.get_attribute('src'))


#获取标签ID,位置,名称,大小(了解)
print(tag.id)
print(tag.location)
print(tag.tag_name)
print(tag.size)


browser.close()

澳门葡京备用网址 111澳门葡京备用网址 112

7、等待成分加载

#!/usr/bin/env python
# -*- coding:utf-8 -*-
from twisted.internet import defer
from twisted.web.client import getPage
from twisted.internet import reactor

def one_done(arg):
    print(arg)

def all_done(arg):
    print('done')
    reactor.stop()

@defer.inlineCallbacks
def task(url):
    res = getPage(bytes(url, encoding='utf8')) # 发送Http请求
    res.addCallback(one_done)
    yield res

url_list = [
    'http://www.cnblogs.com',
    'http://www.cnblogs.com',
    'http://www.cnblogs.com',
    'http://www.cnblogs.com',
]

defer_list = [] # [特殊,特殊,特殊(已经向url发送请求)]
for url in url_list:
    v = task(url)
    defer_list.append(v)

d = defer.DeferredList(defer_list)
d.addBoth(all_done)
reactor.run() # 死循环,事件循环

     
a、selenium只是模仿浏览器的行为,而浏览器解析页面是内需时刻的(执行css,js),一些成分恐怕需求过一段时间才能加载出来,为了保证能查找到成分,必须等待

twisted

      b、等待的法门分二种:

澳门葡京备用网址 113澳门葡京备用网址 114

               
隐式等待:在browser.get(’xxx’)前就安装,针对具有因素有效

 1 #!/usr/bin/env python
 2 # -*- coding:utf-8 -*-
 3 from tornado.httpclient import AsyncHTTPClient
 4 from tornado.httpclient import HTTPRequest
 5 from tornado import ioloop
 6 
 7 COUNT = 0
 8 def handle_response(response):
 9     global COUNT
10     COUNT -= 1
11     if response.error:
12         print("Error:", response.error)
13     else:
14         print(response.body)
15         # 方法同twisted
16         # ioloop.IOLoop.current().stop()
17 
18     if COUNT == 0:
19         ioloop.IOLoop.current().stop()
20 
21 def func():
22     url_list = [
23         'http://www.baidu.com',
24         'http://www.bing.com',
25     ]
26     global COUNT
27     COUNT = len(url_list)
28     for url in url_list:
29         print(url)
30         http_client = AsyncHTTPClient()
31         http_client.fetch(HTTPRequest(url), handle_response)
32 
33 ioloop.IOLoop.current().add_callback(func)
34 ioloop.IOLoop.current().start() # 死循环

       
 显式等待:在browser.get(’xxx’)之后设置,只针对有些成分有效

tornado

捌 、爬虫之解析库—-re,beautifulsoup、pyquery

– asyncio

      a、介绍

  • 示例1:asyncio.sleep(5)
  • 演示2:本人封装Http数据包
  • 示例3:asyncio+aiohttp
    aiohttp模块:封装Http数据包 pip3 install aiohttp
  • 示例4:asyncio+requests
    requests模块:封装Http数据包 pip3 install requests
  • gevent,greenlet+异步IO
    pip3 install greenlet
    pip3 install gevent
  • 示例1:gevent+requests
  • 以身作则2:gevent(协程池,最多发多少个请求)+requests
  • 示例3:gevent+requests => grequests
    pip3 install grequests
Beautiful Soup 是一个可以从HTML或XML文件中提取数据的Python库.它能够通过你喜欢的转换器实现惯用的文档导航,查找,
修改文档的方式.Beautiful Soup会帮你节省数小时甚至数天的工作时间.你可能在寻找 Beautiful Soup3 的文档,Beautiful Soup 3
 目前已经停止开发,官网推荐在现在的项目中使用Beautiful Soup 4, 移植到BS4

效率:gevent > Twisted > Tornado > asyncio

     b、安装

四  socket

安装:Beautifulsoup4
      pip3 install beautifulsoup4
安装解释器:
      Beautiful Soup支持Python标准库中的HTML解析器,还支持一些第三方的解析器,其中一个是 lxml .根据操作系统不同,可以选择下列方法来安装lxml:
  1. socket客户端、服务端
    一而再阻塞
    setblocking(0): 无多少(连接无响应;数据未回到)就报错

玖 、遍历文书档案数

——》http请求精神:阻塞

#遍历文档树:即直接通过标签名字选择,特点是选择速度快,但如果存在多个相同的标签则只返回第一个
#1、用法
#2、获取标签的名称
#3、获取标签的属性
#4、获取标签的内容
#5、嵌套选择
#6、子节点、子孙节点
#7、父节点、祖先节点
#8、兄弟节点
sk = socket.socket()
# 1.连接
sk.connect(('www.baidu.com',80,)) # IO阻塞
print('连接成功了...')

# 2. 连接成功发送消息
sk.send(b'GET / HTTP/1.0\r\nHost:www.baidu.com\r\n\r\n')
# sk.send(b'POST / HTTP/1.0\r\nHost:www.baidu.com\r\n\r\nk1=v1&k2=v2')

# 3. 等待着服务端响应
data = sk.recv(8096) # 响应头,响应体,IO阻塞
print(data)

# 关闭连接
sk.close()

澳门葡京备用网址 115澳门葡京备用网址 116

  1. IO多路复用
    客户端:
    try:
      socket对象1.connet()
      socket对象2.connet()
      socket对象3.connet()
    except Exception as e:
      pass
#遍历文档树:即直接通过标签名字选择,特点是选择速度快,但如果存在多个相同的标签则只返回第一个
html_doc = """
<html><head><title>The Dormouse's story</title></head>
<body>
<p id="my p" class="title"><b id="bbb" class="boldest">The Dormouse's story</b></p>

<p class="story">Once upon a time there were three little sisters; and their names were
<a href="http://example.com/elsie" class="sister" id="link1">Elsie</a>,
<a href="http://example.com/lacie" class="sister" id="link2">Lacie</a> and
<a href="http://example.com/tillie" class="sister" id="link3">Tillie</a>;
and they lived at the bottom of a well.</p>

<p class="story">...</p>
"""

#1、用法
from bs4 import BeautifulSoup
soup=BeautifulSoup(html_doc,'lxml')
# soup=BeautifulSoup(open('a.html'),'lxml')

print(soup.p) #存在多个相同的标签则只返回第一个
print(soup.a) #存在多个相同的标签则只返回第一个

#2、获取标签的名称
print(soup.p.name)

#3、获取标签的属性
print(soup.p.attrs)

#4、获取标签的内容
print(soup.p.string) # p下的文本只有一个时,取到,否则为None
print(soup.p.strings) #拿到一个生成器对象, 取到p下所有的文本内容
print(soup.p.text) #取到p下所有的文本内容
for line in soup.stripped_strings: #去掉空白
    print(line)


'''
如果tag包含了多个子节点,tag就无法确定 .string 方法应该调用哪个子节点的内容, .string 的输出结果是 None,如果只有一个子节点那么就输出该子节点的文本,比如下面的这种结构,soup.p.string 返回为None,但soup.p.strings就可以找到所有文本
<p id='list-1'>
    哈哈哈哈
    <a class='sss'>

            <h1>aaaa</h1>

    </a>
    <b>bbbbb</b>
</p>
'''

#5、嵌套选择
print(soup.head.title.string)
print(soup.body.a.string)


#6、子节点、子孙节点
print(soup.p.contents) #p下所有子节点
print(soup.p.children) #得到一个迭代器,包含p下所有子节点

for i,child in enumerate(soup.p.children):
    print(i,child)

print(soup.p.descendants) #获取子孙节点,p下所有的标签都会选择出来
for i,child in enumerate(soup.p.descendants):
    print(i,child)

#7、父节点、祖先节点
print(soup.a.parent) #获取a标签的父节点
print(soup.a.parents) #找到a标签所有的祖先节点,父亲的父亲,父亲的父亲的父亲...


#8、兄弟节点
print('=====>')
print(soup.a.next_sibling) #下一个兄弟
print(soup.a.previous_sibling) #上一个兄弟

print(list(soup.a.next_siblings)) #下面的兄弟们=>生成器对象
print(soup.a.previous_siblings) #上面的兄弟们=>生成器对象

用法

while True:
r,w,e =
select.select([socket对象1,socket对象2,socket对象3,],[socket对象1,socket对象2,socket对象3,],[],0.05)
r = [socket对象1,] # 表示有人给自个儿发送数据
xx = socket对象1.recv()
w = [socket对象1,] # 表示自己早已和外人创造连接成功:
socket对象1.send(‘”””GET /index HTTP/1.0\r\nHost:
baidu.com\r\n\r\n”””‘)

View Code

———>http请求精神:非阻塞

10、搜索文书档案数

sk = socket.socket()
sk.setblocking(False)
# 1.连接
try:
    sk.connect(('www.baidu.com',80,)) # IO阻塞
    print('连接成功了...')
except BlockingIOError as e:
    print(e)
# 2. 连接成功发送消息
sk.send(b'GET / HTTP/1.0\r\nHost:www.baidu.com\r\n\r\n')
# sk.send(b'POST / HTTP/1.0\r\nHost:www.baidu.com\r\n\r\nk1=v1&k2=v2')

# 3. 等待着服务端响应
data = sk.recv(8096) # IO阻塞
print(data)

# 关闭连接
sk.close()

    a、八种过滤器

3. 
class Foo:
·def fileno(self):
·obj = socket()
·return obj.fileno()
r,w,e = select.select([socket对象?,对象?,对象?,Foo()],[],[])
# 对象必须有: fileno方法,并再次回到二个文件讲述符

#1、五种过滤器: 字符串、正则表达式、列表、True、方法
#1.1、字符串:即标签名
print(soup.find_all('b'))

#1.2、正则表达式
import re
print(soup.find_all(re.compile('^b'))) #找出b开头的标签,结果有body和b标签

#1.3、列表:如果传入列表参数,Beautiful Soup会将与列表中任一元素匹配的内容返回.下面代码找到文档中所有<a>标签和<b>标签:
print(soup.find_all(['a','b']))

#1.4、True:可以匹配任何值,下面代码查找到所有的tag,但是不会返回字符串节点
print(soup.find_all(True))
for tag in soup.find_all(True):
    print(tag.name)

#1.5、方法:如果没有合适过滤器,那么还可以定义一个方法,方法只接受一个元素参数 ,如果这个方法返回 True 表示当前元素匹配并且被找到,如果不是则反回 False
def has_class_but_no_id(tag):
    return tag.has_attr('class') and not tag.has_attr('id')

要点:

     b、find_all

a. select内部:对象.fileno()

澳门葡京备用网址 117澳门葡京备用网址 118

b. Foo()内部封装socket文件讲述符

#2、find_all( name , attrs , recursive , text , **kwargs )
#2.1、name: 搜索name参数的值可以使任一类型的 过滤器 ,字符窜,正则表达式,列表,方法或是 True .
print(soup.find_all(name=re.compile('^t')))

#2.2、keyword: key=value的形式,value可以是过滤器:字符串 , 正则表达式 , 列表, True .
print(soup.find_all(id=re.compile('my')))
print(soup.find_all(href=re.compile('lacie'),id=re.compile('\d'))) #注意类要用class_
print(soup.find_all(id=True)) #查找有id属性的标签

# 有些tag属性在搜索不能使用,比如HTML5中的 data-* 属性:
data_soup = BeautifulSoup('<div data-foo="value">foo!</div>','lxml')
# data_soup.find_all(data-foo="value") #报错:SyntaxError: keyword can't be an expression
# 但是可以通过 find_all() 方法的 attrs 参数定义一个字典参数来搜索包含特殊属性的tag:
print(data_soup.find_all(attrs={"data-foo": "value"}))
# [<div data-foo="value">foo!</div>]

#2.3、按照类名查找,注意关键字是class_,class_=value,value可以是五种选择器之一
print(soup.find_all('a',class_='sister')) #查找类为sister的a标签
print(soup.find_all('a',class_='sister ssss')) #查找类为sister和sss的a标签,顺序错误也匹配不成功
print(soup.find_all(class_=re.compile('^sis'))) #查找类为sister的所有标签

#2.4、attrs
print(soup.find_all('p',attrs={'class':'story'}))

#2.5、text: 值可以是:字符,列表,True,正则
print(soup.find_all(text='Elsie'))
print(soup.find_all('a',text='Elsie'))

#2.6、limit参数:如果文档树很大那么搜索会很慢.如果我们不需要全部结果,可以使用 limit 参数限制返回结果的数量.效果与SQL中的limit关键字类似,当搜索到的结果数量达到 limit 的限制时,就停止搜索返回结果
print(soup.find_all('a',limit=2))

#2.7、recursive:调用tag的 find_all() 方法时,Beautiful Soup会检索当前tag的所有子孙节点,如果只想搜索tag的直接子节点,可以使用参数 recursive=False .
print(soup.html.find_all('a'))
print(soup.html.find_all('a',recursive=False))

'''
像调用 find_all() 一样调用tag
find_all() 几乎是Beautiful Soup中最常用的搜索方法,所以我们定义了它的简写方法. BeautifulSoup 对象和 tag 对象可以被当作一个方法来使用,这个方法的执行结果与调用这个对象的 find_all() 方法相同,下面两行代码是等价的:
soup.find_all("a")
soup("a")
这两行代码也是等价的:
soup.title.find_all(text=True)
soup.title(text=True)
'''

IO多路复用: r,w,e = while 监听多少个socket对象;
异步IO: 非阻塞的socket+IO多路复用

find_all

  • 非阻塞socket
  • select[和谐指标],w,r

     c、find

澳门葡京备用网址 119澳门葡京备用网址 120

澳门葡京备用网址 121澳门葡京备用网址 122

import socket
import select

class HttpRequest:
    def __init__(self,sk,host,callback):
        self.socket = sk
        self.host = host
        self.callback = callback
    def fileno(self):
        return self.socket.fileno()

class HttpResponse:
    def __init__(self,recv_data):
        self.recv_data = recv_data
        self.header_dict = {}
        self.body = None
        self.initialize()

    def initialize(self):
        headers, body = self.recv_data.split(b'\r\n\r\n', 1)
        self.body = body
        header_list = headers.split(b'\r\n')
        for h in header_list:
            h_str = str(h,encoding='utf-8')
            v = h_str.split(':',1)
            if len(v) == 2:
                self.header_dict[v[0]] = v[1]


class AsyncRequest:
    def __init__(self):
        self.conn = []
        self.connection = [] # 用于检测是否已经连接成功

    def add_request(self,host,callback):
        try:
            sk = socket.socket()
            sk.setblocking(0)#false
            sk.connect((host,80,))
        except BlockingIOError as e:
            pass
        request = HttpRequest(sk,host,callback)
        self.conn.append(request)
        self.connection.append(request)

    def run(self):

        while True:
            rlist,wlist,elist = select.select(self.conn,self.connection,self.conn,0.05)
            for w in wlist:
                print(w.host,'连接成功...')
                # 只要能循环到,表示socket和服务器端已经连接成功
                tpl = "GET / HTTP/1.0\r\nHost:%s\r\n\r\n"  %(w.host,)
                w.socket.send(bytes(tpl,encoding='utf-8'))
                self.connection.remove(w)
            for r in rlist:
                # r,是HttpRequest
                recv_data = bytes()
                while True:
                    try:
                        chunck = r.socket.recv(8096)
                        recv_data += chunck
                    except Exception as e:
                        break
                print(r.host,"有数据返回...",recv_data)

                response = HttpResponse(recv_data)
                r.callback(response)
                r.socket.close()
                self.conn.remove(r)
            if len(self.conn) == 0:
                break

def f1(response):
    print('保存到文件',response.header_dict)

def f2(response):
    print('保存到数据库', response.header_dict)

url_list = [
    {'host':'www.baidu.com','callback': f1},
    {'host':'cn.bing.com','callback': f2},
    {'host':'www.cnblogs.com','callback': f2},
]

req = AsyncRequest()
for item in url_list:
    req.add_request(item['host'],item['callback'])

req.run()
#3、find( name , attrs , recursive , text , **kwargs )
find_all() 方法将返回文档中符合条件的所有tag,尽管有时候我们只想得到一个结果.比如文档中只有一个<body>标签,那么使用 find_all() 方法来查找<body>标签就不太合适, 使用 find_all 方法并设置 limit=1 参数不如直接使用 find() 方法.下面两行代码是等价的:

soup.find_all('title', limit=1)
# [<title>The Dormouse's story</title>]
soup.find('title')
# <title>The Dormouse's story</title>

唯一的区别是 find_all() 方法的返回结果是值包含一个元素的列表,而 find() 方法直接返回结果.
find_all() 方法没有找到目标是返回空列表, find() 方法找不到目标时,返回 None .
print(soup.find("nosuchtag"))
# None

soup.head.title 是 tag的名字 方法的简写.这个简写的原理就是多次调用当前tag的 find() 方法:

soup.head.title
# <title>The Dormouse's story</title>
soup.find("head").find("title")
# <title>The Dormouse's story</title>

View Code

View Code

 

     d、css选择器

#1、CSS选择器
print(soup.p.select('.sister'))
print(soup.select('.sister span'))

print(soup.select('#link1'))
print(soup.select('#link1 span'))

print(soup.select('#list-2 .element.xxx'))

print(soup.select('#list-2')[0].select('.element')) #可以一直select,但其实没必要,一条select就可以了

# 2、获取属性
print(soup.select('#list-2 h1')[0].attrs)

# 3、获取内容
print(soup.select('#list-2 h1')[0].get_text())

十一、爬虫之MongoDB

    a、简介

MongoDB是一款强大、灵活、且易于扩充的通用型数据库

    b、易用性

MongoDB是一个面向文档(document-oriented)的数据库,而不是关系型数据库。
不采用关系型主要是为了获得更好得扩展性。当然还有一些其他好处,与关系数据库相比,面向文档的数据库不再有“行“(row)的概念取而代之的是更为灵活的“文档”(document)模型。
通过在文档中嵌入文档和数组,面向文档的方法能够仅使用一条记录来表现复杂的层级关系,这与现代的面向对象语言的开发者对数据的看法一致。
另外,不再有预定义模式(predefined schema):文档的键(key)和值(value)不再是固定的类型和大小。由于没有固定的模式,
根据需要添加或删除字段变得更容易了。通常由于开发者能够进行快速迭代,所以开发进程得以加快。而且,实验更容易进行。开发者能尝试大量的数据模型,从中选一个最好的。

     c、易扩展性

应用程序数据集的大小正在以不可思议的速度增长。随着可用带宽的增长和存储器价格的下降,即使是一个小规模的应用程序,需要存储的数据量也可能大的惊人,甚至超出
了很多数据库的处理能力。过去非常罕见的T级数据,现在已经是司空见惯了。
由于需要存储的数据量不断增长,开发者面临一个问题:应该如何扩展数据库,分为纵向扩展和横向扩展,纵向扩展是最省力的做法,但缺点是大型机一般都非常贵,而且
当数据量达到机器的物理极限时,花再多的钱也买不到更强的机器了,此时选择横向扩展更为合适,但横向扩展带来的另外一个问题就是需要管理的机器太多。
MongoDB的设计采用横向扩展。面向文档的数据模型使它能很容易地在多台服务器之间进行数据分割。MongoDB能够自动处理跨集群的数据和负载,自动重新分配文档,以及将
用户的请求路由到正确的机器上。这样,开发者能够集中精力编写应用程序,而不需要考虑如何扩展的问题。如果一个集群需要更大的容量,只需要向集群添加新服务器,
MongoDB就会自动将现有的数据向新服务器传送

      d、充分的效益

MongoDB作为一款通用型数据库,除了能够创建、读取、更新和删除数据之外,还提供了一系列不断扩展的独特功能
#1、索引
支持通用二级索引,允许多种快速查询,且提供唯一索引、复合索引、地理空间索引、全文索引

#2、聚合
支持聚合管道,用户能通过简单的片段创建复杂的集合,并通过数据库自动优化

#3、特殊的集合类型
支持存在时间有限的集合,适用于那些将在某个时刻过期的数据,如会话session。类似地,MongoDB也支持固定大小的集合,用于保存近期数据,如日志

#4、文件存储
支持一种非常易用的协议,用于存储大文件和文件元数据。MongoDB并不具备一些在关系型数据库中很普遍的功能,如链接join和复杂的多行事务。省略
这些的功能是处于架构上的考虑,或者说为了得到更好的扩展性,因为在分布式系统中这两个功能难以高效地实

      e、特出的个性

MongoDB的一个主要目标是提供卓越的性能,这很大程度上决定了MongoDB的设计。MongoDB把尽可能多的内存用作缓存cache,视图为每次查询自动选择正确的索引。
总之各方面的设计都旨在保持它的高性能
虽然MongoDB非常强大并试图保留关系型数据库的很多特性,但它并不追求具备关系型数据库的所有功能。只要有可能,数据库服务器就会将处理逻辑交给客户端。
这种精简方式的设计是MongoDB能够实现如此高性能的原因之一

十二、MongoDB基础

   
 a、文书档案是MongoDB的主干概念。文档正是键值对的三个一如既往集{‘msg’:’hello’,’foo’:3}。类似于python中的有序字典

需要注意的是:
#1、文档中的键/值对是有序的。
#2、文档中的值不仅可以是在双引号里面的字符串,还可以是其他几种数据类型(甚至可以是整个嵌入的文档)。
#3、MongoDB区分类型和大小写。
#4、MongoDB的文档不能有重复的键。
#5、文档中的值可以是多种不同的数据类型,也可以是一个完整的内嵌文档。文档的键是字符串。除了少数例外情况,键可以使用任意UTF-8字符。

文档键命名规范:
#1、键不能含有\0 (空字符)。这个字符用来表示键的结尾。
#2、.和$有特别的意义,只有在特定环境下才能使用。
#3、以下划线"_"开头的键是保留的(不是严格要求的)。

澳门葡京备用网址 ,   
b、集合正是一组文书档案。若是将MongoDB中的1个文书档案比喻为关系型数据的一行,那么二个集结就是一定于一张表

#1、集合存在于数据库中,通常情况下为了方便管理,不同格式和类型的数据应该插入到不同的集合,但其实集合没有固定的结构,这意味着我们完全可以把不同格式和类型的数据统统插入一个集合中。

#2、组织子集合的方式就是使用“.”,分隔不同命名空间的子集合。
比如一个具有博客功能的应用可能包含两个集合,分别是blog.posts和blog.authors,这是为了使组织结构更清晰,这里的blog集合(这个集合甚至不需要存在)跟它的两个子集合没有任何关系。
在MongoDB中,使用子集合来组织数据非常高效,值得推荐

#3、当第一个文档插入时,集合就会被创建。合法的集合名:
集合名不能是空字符串""。
集合名不能含有\0字符(空字符),这个字符表示集合名的结尾。
集合名不能以"system."开头,这是为系统集合保留的前缀。
用户创建的集合名字不能含有保留字符。有些驱动程序的确支持在集合名里面包含,这是因为某些系统生成的集合中包含该字符。除非你要访问这种系统创建的集合,否则千万不要在名字里出现$。

    c、数据库:在MongoDB中,八个文书档案组成集合,多个汇聚能够构成数据库

数据库也通过名字来标识。数据库名可以是满足以下条件的任意UTF-8字符串:
#1、不能是空字符串("")。
#2、不得含有' '(空格)、.、$、/、\和\0 (空字符)。
#3、应全部小写。
#4、最多64字节。

有一些数据库名是保留的,可以直接访问这些有特殊作用的数据库。
#1、admin: 从身份认证的角度讲,这是“root”数据库,如果将一个用户添加到admin数据库,这个用户将自动获得所有数据库的权限。
再者,一些特定的服务器端命令也只能从admin数据库运行,如列出所有数据库或关闭服务器
#2、local: 这个数据库永远都不可以复制,且一台服务器上的所有本地集合都可以存储在这个数据库中
#3、config: MongoDB用于分片设置时,分片信息会存储在config数据库中

 
  d、强调:把数据库名添加到集合名前,获得集合的一心限定名,即命名空间

例如:
如果要使用cms数据库中的blog.posts集合,这个集合的命名空间就是
cmd.blog.posts。命名空间的长度不得超过121个字节,且在实际使用中应该小于100个字节

十三、CRUD操作

    a、数据库操作

#1、增
use config #如果数据库不存在,则创建数据库,否则切换到指定数据库。

#2、查
show dbs #查看所有
可以看到,我们刚创建的数据库config并不在数据库的列表中, 要显示它,我们需要向config数据库插入一些数据。
db.table1.insert({'a':1})

#3、删
use config #先切换到要删的库下
db.dropDatabase() #删除当前库

   b、集合操作

#1、增
当第一个文档插入时,集合就会被创建
> use database1
switched to db database1
> db.table1.insert({'a':1})
WriteResult({ "nInserted" : 1 })
> db.table2.insert({'b':2})
WriteResult({ "nInserted" : 1 })

#2、查
> show tables
table1
table2

#3、删
> db.table1.drop()
true
> show tables
table2

   d、文书档案操作

   增:

   查:

澳门葡京备用网址 123澳门葡京备用网址 124

# SQL:=,!=,>,<,>=,<=
# MongoDB:{key:value}代表什么等于什么,"$ne","$gt","$lt","gte","lte",其中"$ne"能用于所有数据类型

#1、select * from db1.user where name = "alex";
db.user.find({'name':'alex'})

#2、select * from db1.user where name != "alex";
db.user.find({'name':{"$ne":'alex'}})

#3、select * from db1.user where id > 2;
db.user.find({'_id':{'$gt':2}})

#4、select * from db1.user where id < 3;
db.user.find({'_id':{'$lt':3}})

#5、select * from db1.user where id >= 2;
db.user.find({"_id":{"$gte":2,}})

#6、select * from db1.user where id <= 2;
db.user.find({"_id":{"$lte":2}})

相比运算

澳门葡京备用网址 125澳门葡京备用网址 126

# SQL:and,or,not
# MongoDB:字典中逗号分隔的多个条件是and关系,"$or"的条件放到[]内,"$not"

#1、select * from db1.user where id >= 2 and id < 4;
db.user.find({'_id':{"$gte":2,"$lt":4}})

#2、select * from db1.user where id >= 2 and age < 40;
db.user.find({"_id":{"$gte":2},"age":{"$lt":40}})

#3、select * from db1.user where id >= 5 or name = "alex";
db.user.find({
    "$or":[
        {'_id':{"$gte":5}},
        {"name":"alex"}
        ]
})

#4、select * from db1.user where id % 2=1;
db.user.find({'_id':{"$mod":[2,1]}})

#5、上题,取反
db.user.find({'_id':{"$not":{"$mod":[2,1]}}})

逻辑运算

澳门葡京备用网址 127澳门葡京备用网址 128

# SQL:in,not in
# MongoDB:"$in","$nin"

#1、select * from db1.user where age in (20,30,31);
db.user.find({"age":{"$in":[20,30,31]}})

#2、select * from db1.user where name not in ('alex','yuanhao');
db.user.find({"name":{"$nin":['alex','yuanhao']}}

成员运算

澳门葡京备用网址 129澳门葡京备用网址 130

# SQL: regexp 正则
# MongoDB: /正则表达/i

#1、select * from db1.user where name regexp '^j.*?(g|n)$';
db.user.find({'name':/^j.*?(g|n)$/i})

正则匹配

澳门葡京备用网址 131澳门葡京备用网址 132

#1、查看有dancing爱好的人
db.user.find({'hobbies':'dancing'})

#2、查看既有dancing爱好又有tea爱好的人
db.user.find({
    'hobbies':{
        "$all":['dancing','tea']
        }
})

#3、查看第4个爱好为tea的人
db.user.find({"hobbies.3":'tea'})

#4、查看所有人最后两个爱好
db.user.find({},{'hobbies':{"$slice":-2},"age":0,"_id":0,"name":0,"addr":0})

#5、查看所有人的第2个到第3个爱好
db.user.find({},{'hobbies':{"$slice":[1,2]},"age":0,"_id":0,"name":0,"addr":0})

> db.blog.find().pretty()
{
        "_id" : 1,
        "name" : "alex意外死亡的真相",
        "comments" : [
                {
                        "name" : "egon",
                        "content" : "alex是谁???",
                        "thumb" : 200
                },
                {
                        "name" : "wxx",
                        "content" : "我去,真的假的",
                        "thumb" : 300
                },
                {
                        "name" : "yxx",
                        "content" : "吃喝嫖赌抽,欠下两个亿",
                        "thumb" : 40
                },
                {
                        "name" : "egon",
                        "content" : "xxx",
                        "thumb" : 0
                }
        ]
}
db.blog.find({},{'comments':{"$slice":-2}}).pretty() #查询最后两个
db.blog.find({},{'comments':{"$slice":[1,2]}}).pretty() #查询1到2

询问数组

澳门葡京备用网址 133澳门葡京备用网址 134

# 排序:--1代表升序,-1代表降序
db.user.find().sort({"name":1,})
db.user.find().sort({"age":-1,'_id':1})

排序

澳门葡京备用网址 135澳门葡京备用网址 136

# 分页:--limit代表取多少个document,skip代表跳过前多少个document。 
db.user.find().sort({'age':1}).limit(1).skip(2)

分页

澳门葡京备用网址 137澳门葡京备用网址 138

# 获取数量
db.user.count({'age':{"$gt":30}}) 

--或者
db.user.find({'age':{"$gt":30}}).count()

取得数据

澳门葡京备用网址 139澳门葡京备用网址 140

#1、{'key':null} 匹配key的值为null或者没有这个key
db.t2.insert({'a':10,'b':111})
db.t2.insert({'a':20})
db.t2.insert({'b':null})

> db.t2.find({"b":null})
{ "_id" : ObjectId("5a5cc2a7c1b4645aad959e5a"), "a" : 20 }
{ "_id" : ObjectId("5a5cc2a8c1b4645aad959e5b"), "b" : null }

#2、查找所有
db.user.find() #等同于db.user.find({})
db.user.find().pretty()

#3、查找一个,与find用法一致,只是只取匹配成功的第一个
db.user.findOne({"_id":{"$gt":3}})

杂项

  改:

澳门葡京备用网址 141澳门葡京备用网址 142

update() 方法用于更新已存在的文档。语法格式如下:
db.collection.update(
   <query>,
   <update>,
   {
     upsert: <boolean>,
     multi: <boolean>,
     writeConcern: <document>
   }
)
参数说明:对比update db1.t1 set name='EGON',sex='Male' where name='egon' and age=18;

query : 相当于where条件。
update : update的对象和一些更新的操作符(如$,$inc...等,相当于set后面的
upsert : 可选,默认为false,代表如果不存在update的记录不更新也不插入,设置为true代表插入。
multi : 可选,默认为false,代表只更新找到的第一条记录,设为true,代表更新找到的全部记录。
writeConcern :可选,抛出异常的级别。

更新操作是不可分割的:若两个更新同时发送,先到达服务器的先执行,然后执行另外一个,不会破坏文档。

update语法介绍

澳门葡京备用网址 143澳门葡京备用网址 144

#注意:除非是删除,否则_id是始终不会变的
#1、覆盖式:
db.user.update({'age':20},{"name":"Wxx","hobbies_count":3})
是用{"_id":2,"name":"Wxx","hobbies_count":3}覆盖原来的记录

#2、一种最简单的更新就是用一个新的文档完全替换匹配的文档。这适用于大规模式迁移的情况。例如
var obj=db.user.findOne({"_id":2})

obj.username=obj.name+'SB'
obj.hobbies_count++
delete obj.age

db.user.update({"_id":2},obj)

覆盖式

澳门葡京备用网址 145澳门葡京备用网址 146

#设置:$set

通常文档只会有一部分需要更新。可以使用原子性的更新修改器,指定对文档中的某些字段进行更新。
更新修改器是种特殊的键,用来指定复杂的更新操作,比如修改、增加后者删除

#1、update db1.user set  name="WXX" where id = 2
db.user.update({'_id':2},{"$set":{"name":"WXX",}})

#2、没有匹配成功则新增一条{"upsert":true}
db.user.update({'_id':6},{"$set":{"name":"egon","age":18}},{"upsert":true})

#3、默认只改匹配成功的第一条,{"multi":改多条}
db.user.update({'_id':{"$gt":4}},{"$set":{"age":28}})
db.user.update({'_id':{"$gt":4}},{"$set":{"age":38}},{"multi":true})

#4、修改内嵌文档,把名字为alex的人所在的地址国家改成Japan
db.user.update({'name':"alex"},{"$set":{"addr.country":"Japan"}})

#5、把名字为alex的人的地2个爱好改成piao
db.user.update({'name':"alex"},{"$set":{"hobbies.1":"piao"}})

#6、删除alex的爱好,$unset
db.user.update({'name':"alex"},{"$unset":{"hobbies":""}})

设置:$set

澳门葡京备用网址 147澳门葡京备用网址 148

往数组内添加元素:$push
#1、为名字为yuanhao的人添加一个爱好read
db.user.update({"name":"yuanhao"},{"$push":{"hobbies":"read"}})

#2、为名字为yuanhao的人一次添加多个爱好tea,dancing
db.user.update({"name":"yuanhao"},{"$push":{
    "hobbies":{"$each":["tea","dancing"]}
}})

按照位置且只能从开头或结尾删除元素:$pop
#3、{"$pop":{"key":1}} 从数组末尾删除一个元素

db.user.update({"name":"yuanhao"},{"$pop":{
    "hobbies":1}
})

#4、{"$pop":{"key":-1}} 从头部删除
db.user.update({"name":"yuanhao"},{"$pop":{
    "hobbies":-1}
})

#5、按照条件删除元素,:"$pull" 把符合条件的统统删掉,而$pop只能从两端删
db.user.update({'addr.country':"China"},{"$pull":{
    "hobbies":"read"}
},
{
    "multi":true
}
)

#丰裕删减数组内成分

 删:

澳门葡京备用网址 149澳门葡京备用网址 150

#1、删除多个中的第一个
db.user.deleteOne({ 'age': 8 })

#2、删除国家为China的全部
db.user.deleteMany( {'addr.country': 'China'} ) 

#3、删除全部
db.user.deleteMany({})

View Code

   e、聚合

如果你有数据存储在MongoDB中,你想做的可能就不仅仅是将数据提取出来那么简单了;你可能希望对数据进行分析并加以利用。MongoDB提供了以下聚合工具:
#1、聚合框架
#2、MapReduce(详见MongoDB权威指南)
#3、几个简单聚合命令:count、distinct和group。(详见MongoDB权威指南)

#聚合框架:
可以使用多个构件创建一个管道,上一个构件的结果传给下一个构件。
这些构件包括(括号内为构件对应的操作符):筛选($match)、投射($project)、分组($group)、排序($sort)、限制($limit)、跳过($skip)
不同的管道操作符可以任意组合,重复使用

十四 、爬虫质量

   a、背景知识

   爬虫的本质就是一个socket客户端与服务端的通信过程,如果我们有多个url待爬取,只用一个线程且采用串行的方式执行,
那只能等待爬取一个结束后才能继续下一个,效率会非常低。需要强调的是:对于单线程下串行N个任务,并不完全等同于低效,
如果这N个任务都是纯计算的任务,那么该线程对cpu的利用率仍然会很高,之所以单线程下串行多个爬虫任务低效,
是因为爬虫任务是明显的IO密集型程序。

   b、同步、异步、回调机制

 
 c、协助进行调用:即提交3个任务后就在原地等候职务达成,等到获得义务的结果后再持续下一行代码,效能低下

十五、高性能

 上述无论哪种解决方案其实没有解决一个性能相关的问题:IO阻塞,无论是多进程还是多线程,在遇到IO阻塞时都会被操作系统强行剥夺走CPU的执行权限,
程序的执行效率因此就降低了下来。
    解决这一问题的关键在于,我们自己从应用程序级别检测IO阻塞然后切换到我们自己程序的其他任务执行,这样把我们程序的IO降到最低,
我们的程序处于就绪态就会增多,以此来迷惑操作系统,操作系统便以为我们的程序是IO比较少的程序,从而会尽可能多的分配CPU给我们,这样也就达到了提升程序执行效率的目的

 
 a、在python3.3事后新增了asyncio模块,能够帮我们检查和测试IO(只可以是网络IO),达成应用程序级别的切换

 
 b、
但asyncio模块只好发tcp级其他请求,不可能发http协议,由此,在大家须求发送http请求的时候,须要我们自定义http报头**

** 
 c、
自定义http报头多少有点麻烦,于是有了aiohttp模块,专门帮大家封装http报头,然后我们还亟需用asyncio检查和测试IO实现切换**

** 
 d、除此以外,还能将requests.get函数字传送给asyncio,就可见被检查和测试了**

**   e、再有在此以前在协程时介绍的gevent模块**

**   f、封装了gevent+requests模块的grequests模块**

** 
 g、twisted:是3个网络框架,在那之中多少个效果是出殡和埋葬异步请求,检查和测试IO并自行切换**

**十六、scrapy**

**   a、介绍**

   Scrapy一个开源和协作的框架,其最初是为了页面抓取 (更确切来说, 网络抓取 )所设计的,使用它可以以快速、简单、可扩展的方式从网站中提取所需的数据。
但目前Scrapy的用途十分广泛,可用于如数据挖掘、监测和自动化测试等领域,也可以应用在获取API所返回的数据(例如 Amazon Associates Web Services ) 或者通用的网络爬虫。
    Scrapy 是基于twisted框架开发而来,twisted是一个流行的事件驱动的python网络框架。因此Scrapy使用了一种非阻塞(又名异步)的代码来实现并发

   b、安装

#Windows平台
    1、pip3 install wheel #安装后,便支持通过wheel文件安装软件,wheel文件官网:https://www.lfd.uci.edu/~gohlke/pythonlibs
    3、pip3 install lxml
    4、pip3 install pyopenssl
    5、下载并安装pywin32:https://sourceforge.net/projects/pywin32/files/pywin32/
    6、下载twisted的wheel文件:http://www.lfd.uci.edu/~gohlke/pythonlibs/#twisted
    7、执行pip3 install 下载目录\Twisted-17.9.0-cp36-cp36m-win_amd64.whl
    8、pip3 install scrapy

#Linux平台
    1、pip3 install scrapy

    c、命令行工具

#1 查看帮助
    scrapy -h
    scrapy <command> -h

#2 有两种命令:其中Project-only必须切到项目文件夹下才能执行,而Global的命令则不需要
    Global commands:
        startproject #创建项目
        genspider    #创建爬虫程序
        settings     #如果是在项目目录下,则得到的是该项目的配置
        runspider    #运行一个独立的python文件,不必创建项目
        shell        #scrapy shell url地址  在交互式调试,如选择器规则正确与否
        fetch        #独立于程单纯地爬取一个页面,可以拿到请求头
        view         #下载完毕后直接弹出浏览器,以此可以分辨出哪些数据是ajax请求
        version      #scrapy version 查看scrapy的版本,scrapy version -v查看scrapy依赖库的版本
    Project-only commands:
        crawl        #运行爬虫,必须创建项目才行,确保配置文件中ROBOTSTXT_OBEY = False
        check        #检测项目中有无语法错误
        list         #列出项目中所包含的爬虫名
        edit         #编辑器,一般不用
        parse        #scrapy parse url地址 --callback 回调函数  #以此可以验证我们的回调函数是否正确
        bench        #scrapy bentch压力测试

#3 官网链接
    https://docs.scrapy.org/en/latest/topics/commands.html

   d、文件评释:

  • scrapy.cfg
     项指标主配置消息,用来布局scrapy时行使,爬虫相关的配备消息在settings.py文件中。
  • items.py    设置数据存储模板,用于结构化数据,如:Django的Model
  • pipelines    数据处理作为,如:一般结构化的多少持久化
  • settings.py
    配置文件,如:递归的层数、并发数,延迟下载等。强调:配置文件的选项必须大写不然视为无效,正确写法USE瑞鹰_AGENT=’xxxx’
  • spiders      爬虫目录,如:成立文件,编写爬虫规则

十七、Spiders**

    a、介绍

#1、Spiders是由一系列类(定义了一个网址或一组网址将被爬取)组成,具体包括如何执行爬取任务并且如何从页面中提取结构化的数据。

#2、换句话说,Spiders是你为了一个特定的网址或一组网址自定义爬取和解析页面行为的地方

    b、Spiders会循环做如下事情

#1、生成初始的Requests来爬取第一个URLS,并且标识一个回调函数
第一个请求定义在start_requests()方法内默认从start_urls列表中获得url地址来生成Request请求,默认的回调函数是parse方法。回调函数在下载完成返回response时自动触发

#2、在回调函数中,解析response并且返回值
返回值可以4种:
        包含解析数据的字典
        Item对象
        新的Request对象(新的Requests也需要指定一个回调函数)
        或者是可迭代对象(包含Items或Request)

#3、在回调函数中解析页面内容
通常使用Scrapy自带的Selectors,但很明显你也可以使用Beutifulsoup,lxml或其他你爱用啥用啥。

#4、最后,针对返回的Items对象将会被持久化到数据库
通过Item Pipeline组件存到数据库:https://docs.scrapy.org/en/latest/topics/item-pipeline.html#topics-item-pipeline)
或者导出到不同的文件(通过Feed exports:https://docs.scrapy.org/en/latest/topics/feed-exports.html#topics-feed-exports)
复制代码

    e、Spiders总共提供了五连串

#1、scrapy.spiders.Spider #scrapy.Spider等同于scrapy.spiders.Spider
#2、scrapy.spiders.CrawlSpider
#3、scrapy.spiders.XMLFeedSpider
#4、scrapy.spiders.CSVFeedSpider
#5、scrapy.spiders.SitemapSpider

    f、导入使用

    g、class scrapy.spiders.spider

这是最简单的spider类,任何其他的spider类都需要继承它(包含你自己定义的)。

该类不提供任何特殊的功能,它仅提供了一个默认的start_requests方法默认从start_urls中读取url地址发送requests请求,并且默认parse作为回调函数

十八、Selectors

#1 //与/
#2 text
#3、extract与extract_first:从selector对象中解出内容
#4、属性:xpath的属性加前缀@
#4、嵌套查找
#5、设置默认值
#4、按照属性查找
#5、按照属性模糊查找
#6、正则表达式
#7、xpath相对路径
#8、带变量的xpath

 

 

 

 

 

相关文章

发表评论

电子邮件地址不会被公开。 必填项已用*标注

*
*
Website