、安装
Beautiful Soup 是一个HTML/XML的解析器,主要的功能也是如何解析和提取 HTML/XML 数据。
lxml 只会局部遍历,而Beautiful Soup 是基于HTML DOM的,会载入整个文档,解析整个DOM树,因此时间和内存开销都会大很多,所以性能要低于lxml。
BeautifulSoup 用来解析 HTML 比较简单,API非常人性化,支持CSS选择器、Python标准库中的HTML解析器,也支持 lxml 的 XML解析器。
pip install beautifulsoup4
二、使用案例
from bs4 import BeautifulSoup
import requests
import asyncio
import functools
import re
house_info = []
'''异步请求获取链家每页数据'''
async def get_page(page_index):
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36'
}
request = functools.partial(requests.get, f'https://sh.lianjia.com/ershoufang/pudong/pg{page_index}/',
headers=headers)
loop = asyncio.get_running_loop()
response = await loop.run_in_executor(None, request)
return response
'''使用xpath获取房屋信息'''
def get_house_info(soup):
house_info_list = soup.select('.info') # 房屋title
reg = re.compile(r'\n|\s')
for html in house_info_list:
house_info.append({
'title': re.sub(reg,'',html.select('.title a')[0].getText()),
'house_pattern': re.sub(reg,'',html.select('.houseInfo')[0].getText()),
'price': re.sub(reg,'',html.select('.unitPrice')[0].getText()),
'location': re.sub(reg,'',html.select('.positionInfo')[0].getText()),
'total': re.sub(reg,'',html.select('.totalPrice')[0].getText())
})
'''异步获取第一页数据,拿到第一页房屋信息,并返回分页总数和当前页'''
async def get_first_page():
response = await get_page(1)
soup = BeautifulSoup(response.text, 'lxml')
get_house_info(soup)
print(house_info)
if __name__ == '__main__':
asyncio.run(get_first_page())
三、创建soup对象
soup = BeautifulSoup(markup="", features=None, builder=None,parse_only=None, from_encoding=None, exclude_encodings=None,element_classes=None)
解析器 | 使用方法 | 优势 | 劣势 |
Python标准库 | BeautifulSoup(markup,"html.parser") | Python 的内置标准库、执行速度适中 、文档容错能力强 | Python 2.7.3 or3.2.2) 前的版本中文容错能力差 |
LXML HTML 解析器 | BeautifulSoup(markup,"lxml") | 速度快、文档容错能力强 | 需要安装 C 语言库 |
LXML XML解析器 | BeautifulSoup(markup,"xml") | 速度快、唯一支持 XML 的解析器 | 需要安装 C 语言库 |
html5lib | BeautifulSoup(markup,"html5lib") | 最好的容错性、以浏览器的方式解析文档、生成 HTML5 格式的文档 | 速度慢、不依赖外部扩展 |
四、soup对象
我们需要以客户端的形式通过HTTP协议访问多种服务。比如,下载数据或者同一个基于REST的API进行交互。
对于简单的任务来说,使用urllib.request模块通常就足够了。比方说,要发送一个简单的HTTP GET请求到远端服务器上,可以这样做:
from urllib import request, parse # Base URL being accessed url = 'http://httpbin.org/get' # Dictionary of query parameters (if any) parms = { 'name1' : 'value1', 'name2' : 'value2' } # Encode the query string querystring = parse.urlencode(parms) # Make a GET request and read the response u = request.urlopen(url+'?' + querystring) resp = u.read()
如果需要使用POST方法在请求主体(request body)中发送查询参数,可以将参数编码后作为可选参数提供给urlopen()函数,就像这样:
from urllib import request, parse # Base URL being accessed url = 'http://httpbin.org/post' # Dictionary of query parameters (if any) parms = { 'name1' : 'value1', 'name2' : 'value2' } # Encode the query string querystring = parse.urlencode(parms) # Make a POST request and read the response u = request.urlopen(url, querystring.encode('ascii')) resp = u.read()
如果需要在发出的请求中提供一些自定义的HTTP头,比如修改user-agent字段,那么可以创建一个包含字段值的字典,并创建一个Request实例然后将其传给urlopen()。示例如下:
from urllib import request, parse ... # Extra headers headers = { 'User-agent' : 'none/ofyourbusiness', 'Spam' : 'Eggs' } req = request.Request(url, querystring.encode('ascii'), headers=headers) # Make a request and read the response u = request.urlopen(req) resp = u.read()
如果需要交互的服务比上面的例子都要复杂,也许应该去看看requests库([http://pypi. python.org/pypi/requests](http://pypi. python.org/pypi/requests))。比如,下面这个示例采用requests库重新实现了上面的操作:
import requests # Base URL being accessed url = 'http://httpbin.org/post' # Dictionary of query parameters (if any) parms = { 'name1' : 'value1', 'name2' : 'value2' } # Extra headers headers = { 'User-agent' : 'none/ofyourbusiness', 'Spam' : 'Eggs' } resp = requests.post(url, data=parms, headers=headers) # Decoded text returned by the request text = resp.text
关于requests库,一个值得一提的特性就是它能以多种方式从请求中返回响应结果的内容。从上面的代码来看,resp.text带给我们的是以Unicode解码的响应文本。但是,如果去访问resp.content,就会得到原始的二进制数据。另一方面,如果访问resp.json,那么就会得到JSON格式的响应内容。
下面这个示例利用requests库来发起一个HEAD请求,并从响应中提取出一些HTTP头数据的字段:
import requests resp = requests.head('http://www.python.org/index.html') status = resp.status_code last_modified = resp.headers['last-modified'] content_type = resp.headers['content-type'] content_length = resp.headers['content-length']
下面的示例使用requests库通过基本的认证在Python Package Index(也就是pypi)上执行了一个登录操作:
import requests resp = requests.get('http://pypi.python.org/pypi?:action=login', auth=('user','password'))
下面的示例使用requests库将第一个请求中得到的HTTP cookies传递给下一个请求:
import requests # First request resp1 = requests.get(url) ... # Second requests with cookies received on first requests resp2 = requests.get(url, cookies=resp1.cookies)
最后但也同样重要的是,下面的例子使用requests库来实现内容的上传:
import requests url = 'http://httpbin.org/post' files = { 'file': ('data.csv', open('data.csv', 'rb')) } r = requests.post(url, files=files)
对于确实很简单的HTTP客户端代码,通常使用内建的urllib模块就足够了。但是,如果要做的不仅仅只是简单的GET或POST请求,那就真的不能再依赖它的功能了。这时候就是第三方模块比如requests大显身手的时候了。
举个例子,如果我们决定坚持使用标准的程序库而不考虑像requests这样的第三方库,那么也许就不得不使用底层的http.client模块来实现自己的代码。比方说,下面的代码展示了如何执行一个HEAD请求:
from http.client import HTTPConnection from urllib import parse c = HTTPConnection('www.python.org', 80) c.request('HEAD', '/index.html') resp = c.getresponse() print('Status', resp.status) for name, value in resp.getheaders(): print(name, value)
同样地,如果必须编写涉及代理、认证、cookies以及其他一些细节方面的代码,那么使用urllib就显得特别别扭和啰嗦。比方说,下面这个示例实现在Python package index上的认证:
import urllib.request auth = urllib.request.HTTPBasicAuthHandler() auth.add_password('pypi','http://pypi.python.org','username','password') opener = urllib.request.build_opener(auth) r = urllib.request.Request('http://pypi.python.org/pypi?:action=login') u = opener.open(r) resp = u.read() # From here. You can access more pages using opener ...
坦白说,所有这些操作在requests库中都变得简单得多。
在开发过程中测试HTTP客户端代码常常是很令人沮丧的,因为所有棘手的细节问题都需要考虑(例如cookies、认证、HTTP头、编码方式等)。要完成这些任务,考虑使用httpbin服务(http://httpbin.org)。这个站点会接收发出的请求,然后以JSON的形式将响应信息回传回来。下面是一个交互式的例子:
>>> import requests >>> r = requests.get('http://httpbin.org/get?name=Dave&n=37', ... headers = { 'User-agent': 'goaway/1.0' }) >>> resp = r.json >>> resp['headers'] {'User-Agent': 'goaway/1.0', 'Content-Length': '', 'Content-Type': '', 'Accept-Encoding': 'gzip, deflate, compress', 'Connection': 'keep-alive', 'Host': 'httpbin.org', 'Accept': '*/*'} >>> resp['args'] {'name': 'Dave', 'n': '37'} >>>
在要同一个真正的站点进行交互前,先在httpbin.org这样的网站上做实验常常是可取的办法。尤其是当我们面对3次登录失败就会关闭账户这样的风险时尤为有用(不要尝试自己编写HTTP认证客户端来登录你的银行账户)。
尽管本节没有涉及,requests库还对许多高级的HTTP客户端协议提供了支持,比如OAuth。requests模块的文档(http://docs.python-requests.org)质量很高(坦白说比在这短短一节的篇幅中所提供的任何信息都好),可以参考文档以获得更多的信息。
起因:因为公司遇到发稿问题,很多人喜欢用word编码,然后再发布到网站上。PHP的包中虽然有部分可以使用的类库,但是对于图片始终处理不好,我就想到了python。研究了下,python将word转为html还真是方便。但是,怎么结合到服务器上呢?我们的服务器是用PHP开发的。
1:python脚本
#!/usr/bin/python# -*- coding: UTF-8 -*-import sysfrom pydocx import PyDocXreload(sys)sys.setdefaultencoding('utf8')FileName = sys.argv[1] #获取文件名参数ShortName = sys.argv[2] #获取文件名参数html = PyDocX.to_html(FileName) # f = open("/www/wwwroot/micuer.com/pythoncode/runtime/99.txt", 'w') #服务器的全路径# f.write(html)# f.close()print(html)
2:php处理脚本
public function uploadword(){ try { $file = request()->file("file"); // 上传到本地服务器 $savename = \think\facade\Filesystem::disk('upload')->putFile( 'word', $file); $shotrname = time().".txt"; // 短名称 $savename = "/www/wwwroot/micuer.com/data/upload/".$savename; //Request::domain(). $python_file_name = "/www/wwwroot/micuer.com/pythoncode/WordToHtml.py"; //组装命令 $cmd = "python {$python_file_name} ".$savename." {$shotrname} 2>error.txt 2>&1"; $res = exec($cmd,$array, $ret); return json(["code"=>200,"msg"=>"成功","data"=>$savename,"cmd"=>$cmd,"array"=>$array]); } catch (think\exception\ValidateException $e) { return json(["code"=>40000,"msg"=>$e->getMessage()]); } }
上传界面如下:
实现的功能就是利用PHP的exec函数,调用py脚本,将html代码返回给前台服务器。
返回数据如下
其实,再处理这个方案中,也遇到了很多问题,比如在命令行下只能成功,但是exec函数执行不成功等等。
参考了资料:https://my.oschina.net/u/4427610/blog/3155816
也就是
exec("python python_test.py 2>error.txt 2>&1", $array, $ret);
在bash中0,1,2三个数字分代表STDIN_FILENO、STDOUT_FILENO、STDERR_FILENO,即标准输入(一般是键盘),标准输出(一般是显示屏,准确的说是用户终端控制台),标准错误(出错信息输出)。
也可以通过以下方式将标准错误重定向到标准输出保存到$array中:
打印之后,发现是没有权限调用。于是就直接改为输出了,也就是 py的print(html)函数。
注意几点:
1:执行权限问题
2:exec(“python python_test.py 2>error.txt 2>&1”, $array, $ret); 中 $array就接受到了 print(html)的值
3:各个脚本尽量使用全路径
*请认真填写需求信息,我们会在24小时内与您取得联系。