python多进程

创建子进程

from  multiprocessing import Process
import os

#子进程要执行的代码
def run_proc(name):
    print('Run child process %s (%s)...' % (name, os.getpid()))

if __name__=='__main__':
 	print('Parent process %s.' % os.getpid())
 	p = Process(target=run_proc, args=('test',))
 	print('Child process will start.')
 	p.start()
 	p.join()
 	print('Child process end.')

image.png

用Process来创建子进程

Process([group [, target [, name [, args [, kwargs]]]]]) 

target表示调用对象,args表示调用对象的位置参数元组。kwargs表示调用对象的字典。Name为别名。Group实质上不使用。

进程池

from multiprocessing import Pool
import os, time, random

def log_time_task(name):
	print('Run task %s (%s)...' % (name, os.getpid()))
	start = time.time()
	time.sleep(random.random()*3)
	end = time.time()
	print('Task %s runs %0.2f seconds.' % (name, (end-start)))

if __name__=='__main__':
	print('Parent process %s.' % os.getpid())
	#pool默认大小是cpu核数
	p = Pool(4)
	for i in range(5):
		#pool.apply_async()用来向进程池提交目标请求
		p.apply_async(log_time_task, args=(i,))
	print('Wating for all subprocess done.')
	p.close()
	#pool.join()是用来等待进程池中的worker进程执行完毕,防止主进程在worker进程结束前结束.
	#但pool.join()必须使用在pool.close()或者pool.terminate()之后.
	#其中close()跟terminate()的区别在于close()会等待池中的worker进程执行结束再关闭pool,而terminate()则是直接关闭.
	p.join()
	print('All subprocesses done.')

	#result.successful()表示整个调用执行的状态,如果还有worker没有执行完,则会抛出AssertionError异常。
	#利用multiprocessing下的Pool可以很方便的同时自动处理几百或者上千个并行操作,脚本的复杂性也大大降低。

image.png

pool默认大小是cpu核数

pool.join()是用来等待进程池中的worker进程执行完毕,防止主进程在worker进程结束前结束. 

但pool.join()必须使用在pool.close()或者pool.terminate()之后. 

其中close()跟terminate()的区别在于close()会等待池中的worker进程执行结束再关闭pool,而terminate()则是直接关闭.

result.successful()表示整个调用执行的状态,如果还有worker没有执行完,则会抛出AssertionError异常。 

利用multiprocessing下的Pool可以很方便的同时自动处理几百或者上千个并行操作,脚本的复杂性也大大降低。

子进程

很多时候子进程并不是自身,而是一个外部进程。我们创建了子进程后,还需要进程的输入和输出。

subprocess模块可以让我们非常方便的启动一个子进程,然后控制其输入输出。

进程间通信

http://www.liaoxuefeng.com/wiki/0014316089557264a6b348958f449949df42a6d3a2e542c000/001431927781401bb47ccf187b24c3b955157bb12c5882d000

python字符串前面加u,r,b的含义

u/U:表示unicode字符串 
不是仅仅是针对中文, 可以针对任何的字符串,代表是对字符串进行unicode编码。 
一般英文字符在使用各种编码下, 基本都可以正常解析, 所以一般不带u;但是中文, 必须表明所需编码, 否则一旦编码转换就会出现乱码。 
建议所有编码方式采用utf8

r/R:非转义的原始字符串 
与普通字符相比,其他相对特殊的字符,其中可能包含转义字符,即那些,反斜杠加上对应字母,表示对应的特殊含义的,比如最常见的”\n”表示换行,”\t”表示Tab等。而如果是以r开头,那么说明后面的字符,都是普通的字符了,即如果是“\n”那么表示一个反斜杠字符,一个字母n,而不是表示换行了。 
以r开头的字符,常用于正则表达式,对应着re模块。

b:bytes 
python3.x里默认的str是(py2.x里的)unicode, bytes是(py2.x)的str, b”“前缀代表的就是bytes 
python2.x里, b前缀没什么具体意义, 只是为了兼容python3.x的这种写法

Python3 第三方库记录

记录一些不熟的 python3 的第三方库

不定时更新

Requests

Requests 是用Python语言编写,基于 urllib,采用 Apache2 Licensed 开源协议的 HTTP 库。它比 urllib 更加方便,可以节约我们大量的工作,完全满足 HTTP 测试需求。Requests 的哲学是以 PEP 20 的习语为中心开发的,所以它比 urllib 更加 Pythoner。更重要的一点是它支持 Python3 哦

使用方法:http://blog.csdn.net/shanzhizi/article/details/50903748

======================================================================================

monkey patch

http://blog.leokim.cn/2017/05/15/%E7%8C%B4%E5%AD%90%E8%A1%A5%E4%B8%81monkey-patch/

http://www.liaoxuefeng.com/wiki/001374738125095c955c1e6d8bb493182103fac9270762a000/001407503089986d175822da68d4d6685fbe849a0e0ca35000

======================================================================================

gevent

gevent是基于协程的Python网络库

包含的特性:

1.基于libev的快速事件循环

2.基于greenlet的轻量级执行单元

3.重用Python标准库且概念相似的API

4.支持SSL的协作socket

5.通过c-ares或者线程池进行DNS查询

6.使用标准库和第三方库中使用了阻塞socket的代码的能力

支持Python版本:

>=2.6 和>=3.3

gevent是Python世界中最重要的异步网络库,可以大幅度提高系统的性能。最可贵的是,它允许我们几乎不修改代码,把同步程序变为异步程序。使用的技术就是我们之前讲过的monkey patch。

======================================================================================

asyncio

asyncio是在python3.4中被引进的异步IO库。

你也可以通过python3.3的pypi来安装它。

它相当的复杂,而且我不会介绍太多的细节。

相反,我将会解释你需要知道些什么,以利用它来写异步的代码。 

简而言之,有两件事情你需要知道:协同程序和事件循环。

协同程序像是方法,但是它们可以在代码中的特定点暂停和继续。

当在等待一个IO(比如一个HTTP请求),同时执行另一个请求的时候,可以用来暂停一个协同程序。

我们使用关键字yield from来设定一个状态,表明我们需要一个协同程序的返回值。

而事件循环则被用来安排协同程序的执行。

======================================================================================

Logging

This module defines functions and classes which implement a flexible event logging system for applications and libraries.

The key benefit of having the logging API provided by a standard library module is that all Python modules can participate in logging, so your application log can include your own messages integrated with messages from third-party modules.

======================================================================================

ghost


这个库以前用过,能等页面所有的ajax返回完毕之后在爬取页面

pornhub爬虫

看到有人用2.7写了一个然后我想我用3写一个吧 

本来以为爬下的720P的路径有用呢 后来爬了块1W条信息之后才发现 原来只是个CDN链接

每过一段时间视频后面的参数就会变掉 如果参数不对 视频就没有办法访问到

先当练手吧 后面要做的话 就不抓全部了 每天监控首页前10好了

把每天的前10 DWON下来

#! /usr/bin/env python
# -*- coding:utf8 -*-
# __author__="leokim"
from bs4 import BeautifulSoup

import re
from html.parser import HTMLParser
import urllib.request, urllib.parse, http.cookiejar
import http.cookiejar
import string
import codecs
import time
import pymysql
import json

conn=pymysql.connect(host='localhost',user='root',passwd='superhero',db='python_test',port=3306,charset='utf8')
cur=conn.cursor()#获取一个游标

hosturl = 'https://www.pornhub.com'
login_url = 'https://www.pornhub.com/front/authenticate'


def do_login(login_url):
	headers = {
		'Accept':'application/json, text/javascript, */*; q=0.01',
		'Accept-Encoding':'gzip, deflate, br',
		'Accept-Language':'zh-CN,zh;q=0.8,en;q=0.6',
		'Connection':'keep-alive',
		'Content-Length':'228',
		'Content-Type':'application/x-www-form-urlencoded; charset=UTF-8',
		'Cookie':'platform=pc; ss=552610383637701883; bs=09uvdhtd0plqvxtywxkan6d3u9aahn2f; RNLBSERVERID=ded6752; FastPopSessionRequestNumber=1; desired_username=iamsuperhero%7Cjl6668029%40sina.com; performance_timing=home; expiredEnterModalShown=1; _gat=1; _ga=GA1.2.1975907893.1494638097; _gid=GA1.2.1534576924.1494686000; FPSRN=1',
		'Host':'www.pornhub.com',
		'Origin':'https://www.pornhub.com',
		'Referer':'https://www.pornhub.com/login',
		'User-Agent':'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36',
		'X-Requested-With':'XMLHttpRequest'
	}

	postData = {'loginPage': '1',
	            'redirect': 'RF9jCo8gyhc9dJlS3QxiVEXNRoQCaw8_FuezIgPRrCQ.',
	            'token': 'MTQ5NDY4NTk0OUGXghl0xCEzQMdNs0i7F3J0fV51kVyaf8XXuqe-IBB7f75TJKnNC0tDnS9uh4r1yC8SSDcZ27q-HIkNAOWrdyo.',
	            'username': 'iamsuperhero',
	            'password': '281274954',
	            'remember_me' : 'on'
	            }

	postData = urllib.parse.urlencode(postData).encode('utf-8');

	request = urllib.request.Request(login_url, postData, headers)
	response = urllib.request.urlopen(request)

def get_page_soup(hosturl):
	try:
		cj = http.cookiejar.CookieJar()
		opener = urllib.request.build_opener(urllib.request.HTTPCookieProcessor(cj))
		opener.addheaders = [('User-Agent','Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2272.101 Safari/537.36'),
		    ('Cookie', 'platform=pc; ss=552610383637701883; bs=09uvdhtd0plqvxtywxkan6d3u9aahn2f; RNLBSERVERID=ded6752; FastPopSessionRequestNumber=1; desired_username=iamsuperhero%7Cjl6668029%40sina.com; performance_timing=home; _gat=1; _ga=GA1.2.1975907893.1494638097; _gid=GA1.2.1534576924.1494686000; FPSRN=1; il=v11aG6sqWdiUQBzr4O6rEiEnSCWKAR3eEfgwd6hvbe1GAxNDk3MzY0MzY1MzI3MDY0MzkxO1VpbUxtSmlvUDJPZUZtNFZoN19kQVBkS05mcTNEdF84cXZJRVdDajM1eVUu; expiredEnterModalShown=1')]


		urllib.request.install_opener(opener)
		html_bytes = urllib.request.urlopen(hosturl).read()
		html_string = html_bytes.decode('utf-8')

		return BeautifulSoup(html_string, 'html.parser')
	except:
		return False
	

def get_video_list(soup):
	try:
		video_boxs = soup.find("ul",class_="search-video-thumbs").find_all("",class_="videoBox")
		for video in video_boxs:
			a = video.find("a")
			url = hosturl+a["href"];
			img = a.find("img")

			img_src = img["data-mediumthumb"]
			img_media = img["data-mediabook"]
			video_720p = get_video_hd_addr(url)

			#插入数据库
			sql = "INSERT INTO `pornhub` (`url`,`img_src`,`img_media`,`video_720p`) VALUES (%s,%s,%s,%s)"
			cur.execute(sql, (url,img_src,img_media,video_720p))
	except:
		return 'error'

		# print('###################################################')
		# print(img_src)
		# print(img_media)
		# print(video_720p)
		# print('###################################################')

def get_next_page_url(soup):
	#page_next
	last_page_a = soup.find("li",class_="page_next")
	if(last_page_a is not None):
		a = last_page_a.find("a")
		href = a["href"]
		return href
	else:
		return False

def check_login(check_url):
	soup = get_page_soup(check_url)
	notification = soup.find(id="notificationIcons")
	return notification


def get_video_hd_addr(video_url):
	print(video_url)
	soup = get_page_soup(video_url)
	a_list = soup.find_all("a",class_="downloadBtn")
	for a in a_list:
		if(a.contents[2].strip() == '720p'):
			return a["href"]
			

video_page_url = hosturl+'/video?hd=1&page=1'

#登录验证
if(check_login(video_page_url)):
	print('状态: 已登录.')
	print('=============================')
else:
	do_login(login_url)



url = hosturl+'/video?hd=1&page=1'
tag=True
while(tag):
	soup = get_page_soup(url)

	if(soup):
		next_page_url = get_next_page_url(soup)
		if(next_page_url):
			get_video_list(soup)
			# video_list = get_video_list(soup)
			# for video_page in video_list:
			# 	print(video_page)
			url = hosturl+next_page_url
		else:
			tag=False
			print('程序执行完毕.')
	

Beautiful Soup 常用笔记

记一些bs4常用的东西

终点是后面的css选择器 很方便

对象的种类

1.Tag

2.NavigableString

3.BeautifulSoup

4.Comment


Tag

tag对象与XML或HTML原生文档中的tag相同,主要属性是name,attributes

tag.name
#u'b'

一个tag可能有很多个属性. tag <b class="boldest"> 有一个 “class” 的属性,值为 “boldest” . tag的属性的操作方法与字典相同:

tag['class']# u'boldest'

也可以直接”点”取属性, 比如: .attrs :

tag.attrs# {u'class': u'boldest'}

tag的属性可以被添加,删除或修改. tag的属性操作方法与字典一样

tag['class'] = 'verybold'
tag['id'] = 1
tag
# <blockquote class="verybold" id="1">Extremely bold</blockquote>

del tag['class']
del tag['id']
tag
# <blockquote>Extremely bold</blockquote>

tag['class']
# KeyError: 'class'
print(tag.get('class'))
# None

NavigableString 

BeautifulSoup用NavigableString 来包装tag中的字符串

tag.string
# u'Extremely bold'
type(tag.string)
# <class 'bs4.element.NavigableString'>

一个 NavigableString 字符串与Python中的Unicode字符串相同,并且还支持包含在 遍历文档树 和 搜索文档树 中的一些特性. 通过 unicode() 方法可以直接将 NavigableString对象转换成Unicode字符串

BeautifulSoup

BeautifulSoup 对象表示的是一个文档的全部内容.大部分时候,可以把它当作 Tag 对象,它支持 遍历文档树 和 搜索文档树 中描述的大部分的方法.

因为 BeautifulSoup 对象并不是真正的HTML或XML的tag,所以它没有name和attribute属性.但有时查看它的 .name 属性是很方便的,所以 BeautifulSoup 对象包含了一个值为 “[document]” 的特殊属性 .name

soup.name
# u'[document]'

遍历文档树

一个Tag可能包含多个字符串或其它的Tag,这些都是这个Tag的子节点.Beautiful Soup提供了许多操作和遍历子节点的属性.

注意: Beautiful Soup中字符串节点不支持这些属性,因为字符串没有子节点

.contents 和 .children

tag的 .contents 属性可以将tag的子节点以列表的方式输出:

我觉得不太好用

还不如直接find_all之后for出来呢

parent

获取父节点

兄弟节点 —  .next_sibling 和 .previous_sibling

find_all()

find_all( name , attrs , recursive , string , **kwargs )

find_all() 方法搜索当前tag的所有tag子节点,并判断是否符合过滤器的条件.这里有几个例子:

soup.find_all("title")
# [<title>The Dormouse's story</title>]

soup.find_all("p", "title")
# [<p class="title"><b>The Dormouse's story</b></p>]

soup.find_all("a")
# [<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>,
#  <a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>,
#  <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>]

soup.find_all(id="link2")
# [<a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>]

import re
soup.find(string=re.compile("sisters"))
# u'Once upon a time there were three little sisters; and their names were\n'

有几个方法很相似,还有几个方法是新的,参数中的 string 和 id 是什么含义? 为什么 find_all("p", "title") 返回的是CSS Class为”title”的<p>标签? 我们来仔细看一下 find_all() 的参数

soup.find_all(id='link2')
# [<a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>]

如果传入 href 参数,Beautiful Soup会搜索每个tag的”href”属性:

soup.find_all(href=re.compile("elsie"))
# [<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>]

搜索指定名字的属性时可以使用的参数值包括 字符串 , 正则表达式 , 列表, True .

下面的例子在文档树中查找所有包含 id 属性的tag,无论 id 的值是什么:

soup.find_all(id=True)
# [<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>,
#  <a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>,
#  <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>]

使用多个指定名字的参数可以同时过滤tag的多个属性:

soup.find_all(href=re.compile("elsie"), id='link1')
# [<a class="sister" href="http://example.com/elsie" id="link1">three</a>]

有些tag属性在搜索不能使用,比如HTML5中的 data-* 属性:

data_soup = BeautifulSoup('<div data-foo="value">foo!</div>')
data_soup.find_all(data-foo="value")
# SyntaxError: keyword can't be an expression

但是可以通过 find_all() 方法的 attrs 参数定义一个字典参数来搜索包含特殊属性的tag:

data_soup.find_all(attrs={"data-foo": "value"})# [<div data-foo="value">foo!</div>]

按CSS搜索

按照CSS类名搜索tag的功能非常实用,但标识CSS类名的关键字 class 在Python中是保留字,使用 class 做参数会导致语法错误.从Beautiful Soup的4.1.1版本开始,可以通过 class_参数搜索有指定CSS类名的tag:

soup.find_all("a", class_="sister")
# [<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>,
#  <a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>,
#  <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>]

class_ 参数同样接受不同类型的 过滤器 ,字符串,正则表达式,方法或 True :

soup.find_all(class_=re.compile("itl"))
# [<p class="title"><b>The Dormouse's story</b></p>]

def has_six_characters(css_class):
    return css_class is not None and len(css_class) == 6

#这TM也行???我惊了
soup.find_all(class_=has_six_characters)
# [<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>,
#  <a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>,
#  <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>]

tag的 class 属性是 多值属性 .按照CSS类名搜索tag时,可以分别搜索tag中的每个CSS类名:

css_soup = BeautifulSoup('<p class="body strikeout"></p>')
css_soup.find_all("p", class_="strikeout")
# [<p class="body strikeout"></p>]

css_soup.find_all("p", class_="body")
# [<p class="body strikeout"></p>]

搜索 class 属性时也可以通过CSS值完全匹配:

css_soup.find_all("p", class_="body strikeout")
# [<p class="body strikeout"></p>]

完全匹配 class 的值时,如果CSS类名的顺序与实际不符,将搜索不到结果:

soup.find_all("a", attrs={"class": "sister"})
# [<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>,
#  <a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>,
#  <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>]

string 参数

我看了文档之后还真发现这个参数其实还是有些用处的,比如说直接在find/find_all里通过匹配或者正则搜索string,好像有点逆天····

通过 string 参数可以搜搜文档中的字符串内容.与 name 参数的可选值一样, string 参数接受 字符串 , 正则表达式 , 列表, True . 看例子:

soup.find_all(string="Elsie")
# [u'Elsie']

soup.find_all(string=["Tillie", "Elsie", "Lacie"])
# [u'Elsie', u'Lacie', u'Tillie']

soup.find_all(string=re.compile("Dormouse"))
[u"The Dormouse's story", u"The Dormouse's story"]

def is_the_only_string_within_a_tag(s):
    ""Return True if this string is the only child of its parent tag.""
    return (s == s.parent.string)

soup.find_all(string=is_the_only_string_within_a_tag)
# [u"The Dormouse's story", u"The Dormouse's story", u'Elsie', u'Lacie', u'Tillie', u'...']

虽然 string 参数用于搜索字符串,还可以与其它参数混合使用来过滤tag.Beautiful Soup会找到 .string 方法与 string 参数值相符的tag.下面代码用来搜索内容里面包含“Elsie”的<a>标签:

soup.find_all("a", string="Elsie")
# [<a href="http://example.com/elsie" class="sister" id="link1">Elsie</a>]

其他的参数还有limit,recursive参数

limit就是在find里直接加上limit=n很简单

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

最好用的应该还是CSS选择器 我到目前还没怎么用过 下次写爬虫的时候一定要用了熟悉一下

CSS选择器

Beautiful Soup支持大部分的CSS选择器 http://www.w3.org/TR/CSS2/selector.html , 在 Tag 或 BeautifulSoup 对象的 .select() 方法中传入字符串参数, 即可使用CSS选择器的语法找到tag:

select 返回的是一个tag的list CSS选择器用select这个太方便了!!!!!

soup.select("title")
# [<title>The Dormouse's story</title>]

soup.select("p nth-of-type(3)")
# [<p class="story">...</p>]

通过tag标签逐层查找:

soup.select("body a")
# [<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>,
#  <a class="sister" href="http://example.com/lacie"  id="link2">Lacie</a>,
#  <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>]

soup.select("html head title")
# [<title>The Dormouse's story</title>]

找到某个tag标签下的直接子标签:

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

soup.select("p > a")
# [<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>,
#  <a class="sister" href="http://example.com/lacie"  id="link2">Lacie</a>,
#  <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>]

soup.select("p > a:nth-of-type(2)")
# [<a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>]

soup.select("p > #link1")
# [<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>]

soup.select("body > a")
# []

找到兄弟节点标签:

soup.select("#link1 ~ .sister")
# [<a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>,
#  <a class="sister" href="http://example.com/tillie"  id="link3">Tillie</a>]

soup.select("#link1 + .sister")
# [<a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>]

通过CSS的类名查找:

soup.select(".sister")
# [<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>,
#  <a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>,
#  <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>]

soup.select("[class~=sister]")
# [<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>,
#  <a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>,
#  <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>]

通过tag的id查找:

soup.select("#link1")
# [<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>]

soup.select("a#link2")
# [<a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>]

同时用多种CSS选择器查询元素:

soup.select("#link1,#link2")
# [<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>,
#  <a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>]

通过是否存在某个属性来查找:

soup.select('a[href]')
# [<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>,
#  <a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>,
#  <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>]

通过属性的值来查找:

soup.select('a[href="http://example.com/elsie"]')
# [<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>]

soup.select('a[href^="http://example.com/"]')
# [<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>,
#  <a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>,
#  <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>]

soup.select('a[href$="tillie"]')
# [<a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>]

soup.select('a[href*=".com/el"]')
# [<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>]

基本上用到这些就差不多了 后面有觉得重要的再补充进来

测试文件:

#! /usr/bin/env python
# -*- coding:utf8 -*-
# __author__="leokim"

from bs4 import BeautifulSoup

html_doc = """
<html><head><title>The Dormouse's story</title></head>
    <body>
<p class="title"><b>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>
"""

soup = BeautifulSoup(html_doc, 'html.parser')

#print(type(soup))
#<class 'bs4.BeautifulSoup'>

#print(type(soup.head))
#<class 'bs4.element.Tag'>

#print(type(soup.title))
#<class 'bs4.element.Tag'>


# print(soup.title.string)
#The Dormouse's story
#看来Tag是有string属性的

#print(type(soup.title.string))
#<class 'bs4.element.NavigableString'>

#print(soup.find_all('a'))
#[<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>, <a clas
#s="sister" href="http://example.com/lacie" id="link2">Lacie</a>, <a class="siste
#r" href="http://example.com/tillie" id="link3">Tillie</a>]

#print(type(soup.find_all('a')))
#<class 'bs4.element.ResultSet'>

#print(type(soup.find('a')))
#<class 'bs4.element.Tag'>
#find返回的是Tag 而 find_all返回的是ResultSet

# for a in soup.find_all('a'):
# 	print(type(a))
# 	print(a.string)
#<class 'bs4.element.Tag'>
#<class 'bs4.element.Tag'>
#<class 'bs4.element.Tag'>
#ResoultSet 是Tag的集合可以通过for把Tag单独拿出来之后进行tag相关的操作
#所以我之前用了find_all("xxx").find_all('xxx')是解析不出来的,应该要单独把Tag循环出来操作



#print(type(soup.find('a').string))
#<class 'bs4.element.NavigableString'>

#title_tag = soup.title
#print(title_tag.parent)
#<head><title>The Dormouse's story</title></head>


data_soup = BeautifulSoup('<div data-foo="value">foo!</div>')
data_soup.find_all("data-foo"="value")

通过语言设置来查找:

multilingual_markup = """
 <p>Hello</p>
 <p>Howdy, y'all</p>
 <p>Pip-pip, old fruit</p>
 <p>Bonjour mes amis</p>
"""
multilingual_soup = BeautifulSoup(multilingual_markup)
multilingual_soup.select('p[lang|=en]')
# [<p>Hello</p>,
#  <p>Howdy, y'all</p>,
#  <p>Pip-pip, old fruit</p>]

返回查找到的元素的第一个

soup.select_one(".sister")
# <a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>

对于熟悉CSS选择器语法的人来说这是个非常方便的方法.Beautiful Soup也支持CSS选择器API, 如果你仅仅需要CSS选择器的功能,那么直接使用 lxml 也可以, 而且速度更快,支持更多的CSS选择器语法,但Beautiful Soup整合了CSS选择器的语法和自身方便使用API.

swoole 动态进程池

<?php
/**
 * Created by PhpStorm.
 * User: LeoKim
 * Date: 2017/5/13
 * Time: 9:10
 */

class BaseProcess{
    private $process;

    private $process_list = [];
    private $process_use = [];
    private $min_worker_num = 3;
    private $max_worker_num = 6;

    private $current_num;

    public function __construct()
    {
        $this->process = new swoole_process(array($this, 'run'), false, 2);
        $this->process->start();

        swoole_process::wait();
    }

    public function run($worker)
    {
        $this->current_num = $this->min_worker_num;

        //创建初始进程
        for($i=0; $i < $this->current_num; $i++){
            $process = new swoole_process(array($this, 'task_run'), false, 2);
            $pid = $process->start();

            echo $pid.': 我被初始创建了.'.date('H:i:s').PHP_EOL;
            $this->process_list[$pid] = $process;
            $this->process_use[$pid] = 0;
        }

        foreach($this->process_list as $process){
            //pipe管道被读操作的时候执行闭包的function?
            $this->bind_set_empty($process);
        }

        swoole_timer_tick(1000, function($timer_id){
            static $index=0;
            $index = $index+1;
            $flag = true;

            //我们在前面定义过 当pid对应的值为0的时候表示该进程现在空闲
            foreach($this->process_use as $pid => $used){
                if($used == 0){
                    $flag = false;
                    //我们要使用空闲的进程,把进程标记成工作状态
                    $this->process_use[$pid] = 1;
                    $this->process_list[$pid]->write($index." Hi 我开始工作了.");
                    break;
                }
            }

            //如果没有进程是空闲的, 那么检查进程是否超过最大值,没超过的话创建新的进程
            if($flag && $this->current_num < $this->max_worker_num)
            {
                $process = new swoole_process(array($this, 'task_run'), false, 2);
                $pid = $process->start();
                $this->process_list[$pid] = $process;
                $this->process_use[$pid] = 1 ;
                $this->process_list[$pid]->write($index." Hi 我是新来的,我开始工作了.");
                $this->current_num++;

                $this->bind_set_empty($process);
            }

            //执行n次退出
            if($index==20){
                foreach($this->process_list as $process){
                    foreach($this->process_list as $process){
                        $process->write("任务完毕 我退出了.");
                    }
                    swoole_timer_clear($timer_id);
                    $this->process->exit();
                }
            }
        });
    }

    //进程在创建的时候被执行
    public function task_run($worker)
    {
        //为每个进程绑定回调,当进程执行write的时候触发
        swoole_event_add($worker->pipe, function($pipe) use($worker){
            $data = $worker->read();
            var_dump($worker->pid.": ".$data.' -- '.date('H:i:s'));echo PHP_EOL;
            if($data == '任务完毕 我退出了.')
            {
                $worker->exit();
                exit;
            }

            sleep(5);

            //当worker进程执行write的时候会出发line:43的回调函数
            //把进程标记为空闲
            $worker->write($worker->pid);
        });
    }

    public function bind_set_empty($worker){
        swoole_event_add($worker->pipe, function($pipe) use($worker){
            $pid= $worker->read();
            echo $pid.' 报告,我处理完了之前的任务现在空下来了.'.date('H:i:s').PHP_EOL;
            $this->process_use[$pid] =  0;
        });
    }
}

new BaseProcess();

swoole 简单聊天室

服务端

<?php
/**
 * Created by PhpStorm.
 * User: LeoKim
 * Date: 2017/5/12
 * Time: 23:05
 */

class Server{
    private $serv;
    private $test;

    public function __construct()
    {
        $this->serv = new swoole_server("0.0.0.0", 9502);
        $this->serv->set(
            array(
                'worker_num' => 1,
            )
        );

        $this->serv->on('Start', array($this, 'onStart'));
        $this->serv->on('Connect', array($this, 'onConnect'));
        $this->serv->on('Receive', array($this, 'onReceive'));
        $this->serv->on('Close', array($this, 'onClose'));

        $this->serv->start();
    }

    public function onStart( $serv ) {
        echo "Start\n";
    }

    public function onConnect( $serv, $fd, $from_id ) {
        echo "Client {$fd} connect\n";
    }

    public function onClose( $serv, $fd, $from_id ) {
        echo "Client {$fd} close connection\n";
    }

    public function onReceive( swoole_server $serv, $fd, $from_id, $data){
        echo "Get Message From Client {$fd}:{$data}\n";
        foreach($serv->connections as $client){
            if($fd != $client)
            $serv->send($client, $data);
        }
    }
}

$server = new Server();

客户端

<?php
/**
 * Created by PhpStorm.
 * User: LeoKim
 * Date: 2017/5/12
 * Time: 23:13
 */

$socket = stream_socket_client("tcp://127.0.0.1:9502", $errno, $errstr, 30);

function OnRead()
{
    global $socket;
    $buffer = stream_socket_recvfrom($socket, 1024);
    if(!$buffer)
    {
        echo "Server clised\n";
    }
    echo "\nRECV: {$buffer}\n";
    fwrite(STDOUT, "Enter Msg:");
}

function onWrite()
{
    global $socket;
    echo "on Write\n";
}

function onInput()
{
    global $socket;
    $msg = trim(fgets(STDIN));

    if($msg == 'exit'){
        swoole_event_exit();
        exit();
    }

    swoole_event_write($socket, $msg);
    fwrite(STDOUT, "Enter Msg:");
}

swoole_event_add($socket, 'onRead', 'onWrite');

swoole_event_add(STDIN, 'onInput');

fwrite(STDOUT, "Enter Msg:");

image.png

image.png

image.png

vsftpd设置被动模式

完整配置

listen=yes
listen_port=21
max_clients=100
max_per_ip=10
local_max_rate=5120000
anonymous_enable=no
local_enable=yes
write_enable=no
chroot_local_user=yes
chroot_list_enable=yes
chroot_list_file=/etc/vsftpd/chroot_list
guest_enable=yes
guest_username=kingsoft
virtual_use_local_privs=yes
user_config_dir=/etc/vsftpd/user_config
pasv_enable=yes
pasv_min_port=4500
pasv_max_port=5000
tcp_wrappers=yes
xferlog_enable=yes
xferlog_file=/var/log/ftp/vsftpd.log
idle_session_timeout=600
data_connection_timeout=120
accept_timeout=60
connect_timeout=60
connect_from_port_20=no
local_umask=022
pam_service_name=vsftpd.vu
pasv_address=本机ip
pasv_addr_resolve=yes

主动模式:

Port_enable=YES               开启主动模式
Connect_from_port_20=YES      当主动模式开启的时候 是否启用默认的20端口监听
Ftp_date_port=%portnumber%    上一选项使用NO参数是 指定数据传输端口

被动模式

被动模式
PASV_enable=YES   开启被动模式
PASV_min_port=%number% 被动模式最低端口
PASV_max_port=%number% 被动模式最高端口

iptables中开放这段端口
service iptables start 打开防火墙
iptables -I INPUT  -p tcp  --dport 10020:10040  -j ACCEPT
iptables -A INPUT -p tcp -m tcp --dport 21 -j ACCEPT

在被动模式,服务器做了NAT,例如云主机,这时候我们用特定的IP访问机器,其实还转了一层。FTP客户端访问机器可能会没响应。具体情况为登录成功,但是list目录和文件的时候卡住。

vsftpd   22411   nobody    0u  IPv4  68905      0t0  TCP 10.140.41.65:ftp->10.10.10.98:43380 (ESTABLISHED)
vsftpd   22411   nobody    1u  IPv4  68905      0t0  TCP 10.140.41.65:ftp->10.10.10.98:43380 (ESTABLISHED)

这时候可以看到机器的真正IP。

pasv_address=本机ip【就是我们能访问的外网IP】
pasv_addr_resolve=yes

这样ftp客户端就可以解析IP,访问成功

IO多路复用

image.png

1.epoll函数会监听注册在自己名下的所有的socket描述符

2.当有socket感兴趣的时间发生时,epoll函数才会相应,并返回有时间发生的socket集合

3.epoll的本质是阻塞IO,他的优点在于能同时处理大量socket连接

在这个epoll进程之内同时处理多个描述符

其实并不是异步的

WebSocket 是什么原理(知乎上的很搞笑)

作者:Ovear
链接:https://www.zhihu.com/question/20215561/answer/40316953
来源:知乎

一、WebSocket是HTML5出的东西(协议),也就是说HTTP协议没有变化,或者说没关系,但HTTP是不支持持久连接的(长连接,循环连接的不算)
首先HTTP有1.1和1.0之说,也就是所谓的keep-alive,把多个HTTP请求合并为一个,但是Websocket其实是一个新协议,跟HTTP协议基本没有关系,只是为了兼容现有浏览器的握手规范而已,也就是说它是HTTP协议上的一种补充可以通过这样一张图理解

有交集,但是并不是全部。
另外Html5是指的一系列新的API,或者说新规范,新技术。Http协议本身只有1.0和1.1,而且跟Html本身没有直接关系。。
通俗来说,你可以用HTTP协议传输非Html数据,就是这样=。=
再简单来说,层级不一样

二、Websocket是什么样的协议,具体有什么优点
首先,Websocket是一个持久化的协议,相对于HTTP这种非持久的协议来说。
简单的举个例子吧,用目前应用比较广泛的PHP生命周期来解释。
1) HTTP的生命周期通过Request来界定,也就是一个Request 一个Response,那么HTTP1.0,这次HTTP请求就结束了。
在HTTP1.1中进行了改进,使得有一个keep-alive,也就是说,在一个HTTP连接中,可以发送多个Request,接收多个Response。
但是请记住 Request = Response , 在HTTP中永远是这样,也就是说一个request只能有一个response。而且这个response也是被动的,不能主动发起。

教练,你BB了这么多,跟Websocket有什么关系呢?
_(:з」∠)_好吧,我正准备说Websocket呢。。
首先Websocket是基于HTTP协议的,或者说借用了HTTP的协议来完成一部分握手。
在握手阶段是一样的
——-以下涉及专业技术内容,不想看的可以跳过lol:,或者只看加黑内容——–
首先我们来看个典型的Websocket握手(借用Wikipedia的。。)

GET /chat HTTP/1.1
Host: server.example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==
Sec-WebSocket-Protocol: chat, superchat
Sec-WebSocket-Version: 13
Origin: http://example.com

熟悉HTTP的童鞋可能发现了,这段类似HTTP协议的握手请求中,多了几个东西。
我会顺便讲解下作用。

Upgrade: websocket
Connection: Upgrade

这个就是Websocket的核心了,告诉Apache、Nginx等服务器:注意啦,窝发起的是Websocket协议,快点帮我找到对应的助理处理~不是那个老土的HTTP。

Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==
Sec-WebSocket-Protocol: chat, superchat
Sec-WebSocket-Version: 13

首先,Sec-WebSocket-Key 是一个Base64 encode的值,这个是浏览器随机生成的,告诉服务器:泥煤,不要忽悠窝,我要验证尼是不是真的是Websocket助理。
然后,Sec_WebSocket-Protocol 是一个用户定义的字符串,用来区分同URL下,不同的服务所需要的协议。简单理解:今晚我要服务A,别搞错啦~
最后,Sec-WebSocket-Version 是告诉服务器所使用的Websocket Draft(协议版本),在最初的时候,Websocket协议还在 Draft 阶段,各种奇奇怪怪的协议都有,而且还有很多期奇奇怪怪不同的东西,什么Firefox和Chrome用的不是一个版本之类的,当初Websocket协议太多可是一个大难题。。不过现在还好,已经定下来啦~大家都使用的一个东西~ 脱水:服务员,我要的是13岁的噢→_→

然后服务器会返回下列东西,表示已经接受到请求, 成功建立Websocket啦!

HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: HSmrc0sMlYUkAGmm5OPpG2HaGWk=
Sec-WebSocket-Protocol: chat

这里开始就是HTTP最后负责的区域了,告诉客户,我已经成功切换协议啦~

Upgrade: websocket
Connection: Upgrade

依然是固定的,告诉客户端即将升级的是Websocket协议,而不是mozillasocket,lurnarsocket或者shitsocket。
然后,Sec-WebSocket-Accept 这个则是经过服务器确认,并且加密过后的 Sec-WebSocket-Key。服务器:好啦好啦,知道啦,给你看我的ID CARD来证明行了吧。。
后面的,Sec-WebSocket-Protocol 则是表示最终使用的协议。

至此,HTTP已经完成它所有工作了,接下来就是完全按照Websocket协议进行了。
具体的协议就不在这阐述了。
——————技术解析部分完毕——————

你TMD又BBB了这么久,那到底Websocket有什么鬼用,http long poll,或者ajax轮询不都可以实现实时信息传递么。

好好好,年轻人,那我们来讲一讲Websocket有什么用。
来给你吃点胡(苏)萝(丹)卜(红)

三、Websocket的作用
在讲Websocket之前,我就顺带着讲下 long poll 和 ajax轮询 的原理。
首先是 ajax轮询 ,ajax轮询 的原理非常简单,让浏览器隔个几秒就发送一次请求,询问服务器是否有新信息。
场景再现:
客户端:啦啦啦,有没有新信息(Request)
服务端:没有(Response)
客户端:啦啦啦,有没有新信息(Request)
服务端:没有。。(Response)
客户端:啦啦啦,有没有新信息(Request)
服务端:你好烦啊,没有啊。。(Response)
客户端:啦啦啦,有没有新消息(Request)
服务端:好啦好啦,有啦给你。(Response)
客户端:啦啦啦,有没有新消息(Request)
服务端:。。。。。没。。。。没。。。没有(Response) —- loop

long poll
long poll 其实原理跟 ajax轮询 差不多,都是采用轮询的方式,不过采取的是阻塞模型(一直打电话,没收到就不挂电话),也就是说,客户端发起连接后,如果没消息,就一直不返回Response给客户端。直到有消息才返回,返回完之后,客户端再次建立连接,周而复始。
场景再现
客户端:啦啦啦,有没有新信息,没有的话就等有了才返回给我吧(Request)
服务端:额。。   等待到有消息的时候。。来 给你(Response)
客户端:啦啦啦,有没有新信息,没有的话就等有了才返回给我吧(Request) -loop

从上面可以看出其实这两种方式,都是在不断地建立HTTP连接,然后等待服务端处理,可以体现HTTP协议的另外一个特点,被动性
何为被动性呢,其实就是,服务端不能主动联系客户端,只能有客户端发起。
简单地说就是,服务器是一个很懒的冰箱(这是个梗)(不会、不能主动发起连接),但是上司有命令,如果有客户来,不管多么累都要好好接待。

说完这个,我们再来说一说上面的缺陷(原谅我废话这么多吧OAQ)
从上面很容易看出来,不管怎么样,上面这两种都是非常消耗资源的。
ajax轮询 需要服务器有很快的处理速度和资源。(速度)
long poll 需要有很高的并发,也就是说同时接待客户的能力。(场地大小)
所以ajax轮询 和long poll 都有可能发生这种情况。

客户端:啦啦啦啦,有新信息么?
服务端:月线正忙,请稍后再试(503 Server Unavailable)
客户端:。。。。好吧,啦啦啦,有新信息么?
服务端:月线正忙,请稍后再试(503 Server Unavailable)

客户端:

然后服务端在一旁忙的要死:冰箱,我要更多的冰箱!更多。。更多。。(我错了。。这又是梗。。)

————————–
言归正传,我们来说Websocket吧
通过上面这个例子,我们可以看出,这两种方式都不是最好的方式,需要很多资源。
一种需要更快的速度,一种需要更多的'电话'。这两种都会导致'电话'的需求越来越高。
哦对了,忘记说了HTTP还是一个无状态协议。(感谢评论区的各位指出OAQ)
通俗的说就是,服务器因为每天要接待太多客户了,是个健忘鬼,你一挂电话,他就把你的东西全忘光了,把你的东西全丢掉了。你第二次还得再告诉服务器一遍。

所以在这种情况下出现了,Websocket出现了。
他解决了HTTP的这几个难题。
首先,被动性,当服务器完成协议升级后(HTTP->Websocket),服务端就可以主动推送信息给客户端啦。
所以上面的情景可以做如下修改。
客户端:啦啦啦,我要建立Websocket协议,需要的服务:chat,Websocket协议版本:17(HTTP Request)
服务端:ok,确认,已升级为Websocket协议(HTTP Protocols Switched)
客户端:麻烦你有信息的时候推送给我噢。。
服务端:ok,有的时候会告诉你的。
服务端:balabalabalabala
服务端:balabalabalabala
服务端:哈哈哈哈哈啊哈哈哈哈
服务端:笑死我了哈哈哈哈哈哈哈

就变成了这样,只需要经过一次HTTP请求,就可以做到源源不断的信息传送了。(在程序设计中,这种设计叫做回调,即:你有信息了再来通知我,而不是我傻乎乎的每次跑来问你)
这样的协议解决了上面同步有延迟,而且还非常消耗资源的这种情况。
那么为什么他会解决服务器上消耗资源的问题呢?
其实我们所用的程序是要经过两层代理的,即HTTP协议在Nginx等服务器的解析下,然后再传送给相应的Handler(PHP等)来处理。
简单地说,我们有一个非常快速的接线员(Nginx),他负责把问题转交给相应的客服(Handler)
本身接线员基本上速度是足够的,但是每次都卡在客服(Handler)了,老有客服处理速度太慢。,导致客服不够。
Websocket就解决了这样一个难题,建立后,可以直接跟接线员建立持久连接,有信息的时候客服想办法通知接线员,然后接线员在统一转交给客户。
这样就可以解决客服处理速度过慢的问题了。

同时,在传统的方式上,要不断的建立,关闭HTTP协议,由于HTTP是非状态性的,每次都要重新传输identity info(鉴别信息),来告诉服务端你是谁。
虽然接线员很快速,但是每次都要听这么一堆,效率也会有所下降的,同时还得不断把这些信息转交给客服,不但浪费客服的处理时间,而且还会在网路传输中消耗过多的流量/时间。
但是Websocket只需要一次HTTP握手,所以说整个通讯过程是建立在一次连接/状态中,也就避免了HTTP的非状态性,服务端会一直知道你的信息,直到你关闭请求,这样就解决了接线员要反复解析HTTP协议,还要查看identity info的信息。
同时由客户主动询问,转换为服务器(推送)有信息的时候就发送(当然客户端还是等主动发送信息过来的。。),没有信息的时候就交给接线员(Nginx),不需要占用本身速度就慢的客服(Handler)
——————–
至于怎么在不支持Websocket的客户端上使用Websocket。。答案是:不能
但是可以通过上面说的 long poll 和 ajax 轮询来 模拟出类似的效果