一、引言
社科同胞们一定有过收集/整理数据的经历吧,有时候一些原始数据被存放在大量的 PDF 文件中,例如上市公司公告公报中的各种指标信息,但如何快速地从大量的 PDF 中提取出那些表格却是一个难题。在过往的文章中,我们曾向大家分享过使用 Python 的 pdfplumber 库从 PDF 中读取表格的方法(>>>点击查看“一文读懂如何用python读取并处理PDF中的表格”),但经过长期使用,笔者注意到这个库在默认情况下解析时,对表格的要求非常之高。只有当表格的全部框线都存在时才能发挥作用,如果你要读取的表格框线不全,那么读取时极易丢失部分行或列。后来笔者找到了一个在表格框线不全时也能有不错解析效果的工具库,特此与大家分享使用方法和代码。
Note: kwargs annotated with ^ can only be used with flavor='stream'
and kwargs annotated with * can only be used with flavor='lattice'.
Parameters
----------
filepath : str
Filepath or URL of the PDF file.
pages : str, optional (default: '1')
Comma-separated page numbers.
Example: '1,3,4' or '1,4-end' or 'all'.
password : str, optional (default: None)
Password for decryption.
flavor : str (default: 'lattice')
The parsing method to use ('lattice' or 'stream').
Lattice is used by default.
suppress_stdout : bool, optional (default: True)
Print all logs and warnings.
layout_kwargs : dict, optional (default: {})
A dict of `pdfminer.layout.LAParams <https://github.com/euske/pdfminer/blob/master/pdfminer/layout.py#L33>`_ kwargs.
table_areas : list, optional (default: None)
List of table area strings of the form x1,y1,x2,y2
where (x1, y1) -> left-top and (x2, y2) -> right-bottom
in PDF coordinate space.
columns^ : list, optional (default: None)
List of column x-coordinates strings where the coordinates
are comma-separated.
split_text : bool, optional (default: False)
Split text that spans across multiple cells.
flag_size : bool, optional (default: False)
Flag text based on font size. Useful to detect
super/subscripts. Adds <s></s> around flagged text.
strip_text : str, optional (default: '')
Characters that should be stripped from a string before
assigning it to a cell.
row_tol^ : int, optional (default: 2)
Tolerance parameter used to combine text vertically,
to generate rows.
column_tol^ : int, optional (default: 0)
Tolerance parameter used to combine text horizontally,
to generate columns.
process_background* : bool, optional (default: False)
Process background lines.
line_scale* : int, optional (default: 15)
Line size scaling factor. The larger the value the smaller
the detected lines. Making it very large will lead to text
being detected as lines.
copy_text* : list, optional (default: None)
{'h', 'v'}
Direction in which text in a spanning cell will be copied
over.
shift_text* : list, optional (default: ['l', 't'])
{'l', 'r', 't', 'b'}
Direction in which text in a spanning cell will flow.
line_tol* : int, optional (default: 2)
Tolerance parameter used to merge close vertical and horizontal
lines.
joint_tol* : int, optional (default: 2)
Tolerance parameter used to decide whether the detected lines
and points lie close to each other.
threshold_blocksize* : int, optional (default: 15)
Size of a pixel neighborhood that is used to calculate a
threshold value for the pixel: 3, 5, 7, and so on.
For more information, refer `OpenCV's adaptiveThreshold <https://docs.opencv.org/2.4/modules/imgproc/doc/miscellaneous_transformations.html#adaptivethreshold>`_.
threshold_constant* : int, optional (default: -2)
Constant subtracted from the mean or weighted mean.
Normally, it is positive but may be zero or negative as well.
For more information, refer `OpenCV's adaptiveThreshold <https://docs.opencv.org/2.4/modules/imgproc/doc/miscellaneous_transformations.html#adaptivethreshold>`_.
iterations* : int, optional (default: 0)
Number of times for erosion/dilation is applied.
For more information, refer `OpenCV's dilate <https://docs.opencv.org/2.4/modules/imgproc/doc/filtering.html#dilate>`_.
resolution* : int, optional (default: 300)
Resolution used for PDF to PNG conversion.
Returns
-------
tables : camelot.core.TableList
"""
六、结束语
图片
? 提取 PDF 中的表格是研究工作中的一项基础技术工作,传统的表格解析方法难以解决表格框线不全的问题,所以能解析的表格十分有限,而类似于 camelot 这种基于视觉的表格解析方式则强大很多。不过使用 camelot 时也会遇到一些奇怪的问题,例如目录等一些非表格文本可能也会被识别为表格,所以在应用中还需要根据实际情况选择最合适的工具库。
function my_custom_function() {
return new WP_REST_Response('Hello, this is a custom API response.', 200);
}
7. API 文档与开发工具
官方文档:WordPress REST API 的官方文档提供了全面的使用指南:WordPress REST API 文档
Postman:使用 Postman 等工具可以方便地调试 REST API 请求,测试参数、头部和响应。
通过 WordPress REST API,开发者可以非常灵活地操作 WordPress 网站的数据,构建前后端分离的应用或与其他系统进行集成。
2
Tailwind CSS is the only framework that I've
3
seen scale on large teams. It’s easy to customize,
4
adapts to any design, and the build size is tiny.
5
• 高度(大部分与宽度方法相同):h-[number]
• number 取值 0~96
1
2
Height 24
3
Height 48
4
Height 72
5
Height 96
6
• 全屏高度
1
Height 100vh
(5)布局与定位
• 定位:相对定位与绝对定位
1
2
Parent
3
4
Child
5
6
• 左上角
1
2
3
• 右上角
1
2
3
• 左下角
1
2
3
• 右下角
1
2
3
• 顶边
1
2
3
• 左边
1
2
3
• 右边
1
2
3
• 底边
1
2
3
• 显示方式
1
2
Tailwind CSS is the only framework that
3
(Inline)
4
I've seen scale on large teams.
5
(Inline-block)
6
It’s easy to customize, adapts to any design,
7
(Block)
8
and the build size is tiny.
9
(Hidden)
10
var connListMapUpdate = make(chan int)
func configConnListTunnel(tunnel *net.TCPConn) {
//加锁解决竞争问题
lock.Lock()
used := false
for _, connMatch := range connListMap {
//找到tunnel为nil的而且accept不为nil的connMatch
if connMatch.tunnel == nil && connMatch.accept != nil {
//填充tunnel链路
connMatch.tunnel = tunnel
used = true
//这里要break,是防止这条链路被赋值到多个connMatch!
break
}
}
if !used {
//如果没有被使用的话,则说明所有的connMatch都已经配对好了,直接关闭多余的8008链路
fmt.Println(len(connListMap))
_ = tunnel.Close()
fmt.Println("关闭多余的tunnel")
}
lock.Unlock()
//使用channel机制来告诉另一个方法已经就绪
connListMapUpdate <- UPDATE
}
tcp 转发,这里读取connListMapUpdate这个chain,说明map有更新,需要建立tcpForward隧道
func tcpForward() {
for {
select {
case <-connListMapUpdate:
lock.Lock()
for key, connMatch := range connListMap {
//如果两个都不为空的话,建立隧道连接
if connMatch.tunnel != nil && connMatch.accept != nil {
fmt.Println("建立tcpForward隧道连接")
go joinConn2(connMatch.accept, connMatch.tunnel)
//从map中删除
delete(connListMap, key)
}
}
lock.Unlock()
}
}
}
func joinConn2(conn1 *net.TCPConn, conn2 *net.TCPConn) {
f := func(local *net.TCPConn, remote *net.TCPConn) {
//defer保证close
defer local.Close()
defer remote.Close()
//使用io.Copy传输两个tcp连接,
_, err := io.Copy(local, remote)
if err != nil {
fmt.Println(err.Error())
return
}
fmt.Println("join Conn2 end")
}
go f(conn2, conn1)
go f(conn1, conn2)
}
最后增加一个超时机制,因为会存在这种情况,就是当用户请求8007端口的时候,迟迟等不到配对的connMatch的tunnel链路啊,因为有可能client端挂掉了,导致server不管怎么发送"new"请求,client都无动于衷。
在浏览器看来表现就是一直转着,但是我们不能这样子。
所以当超时的时候,我们主动断掉connMatch中的accept链路即可,设置为5秒
func releaseConnMatch() {
for {
lock.Lock()
for key, connMatch := range connListMap {
//如果在指定时间内没有tunnel的话,则释放该连接
if connMatch.tunnel == nil && connMatch.accept != nil {
if time.Now().Unix()-connMatch.acceptAddTime > 5 {
fmt.Println("释放超时连接")
err := connMatch.accept.Close()
if err != nil {
fmt.Println("释放连接的时候出错了:" + err.Error())
}
delete(connListMap, key)
}
}
}
lock.Unlock()
time.Sleep(5 * time.Second)
}
}
最后把所有方法整合起来
func main() {
//监听控制端口8009
go makeControl()
//监听服务端口8007
go makeAccept()
//监听转发端口8008
go makeForward()
//定时释放连接
go releaseConnMatch()
//执行tcp转发
tcpForward()
}
客户端代码
连接到服务器的8009控制端口,随时接受服务器的控制请求,随时待命
func connectControl() {
var tcpAddr *net.TCPAddr
//这里在一台机测试,所以没有连接到公网,可以修改到公网ip
tcpAddr, _ = net.ResolveTCPAddr("tcp", "127.0.0.1:8009")
conn, err := net.DialTCP("tcp", nil, tcpAddr)
if err != nil {
fmt.Println("Client connect error ! " + err.Error())
return
}
fmt.Println(conn.LocalAddr().String() + " : Client connected!8009")
reader := bufio.NewReader(conn)
for {
s, err := reader.ReadString('\n')
if err != nil || err == io.EOF {
break
} else {
//接收到new的指令的时候,新建一个tcp连接
if s == "new\n" {
go combine()
}
if s == "hi" {
//忽略掉hi的请求
}
}
}
}
combine方法的代码,整合local和remote的tcp连接
func combine() {
local := connectLocal()
remote := connectRemote()
if local != nil && remote != nil {
joinConn(local, remote)
} else {
if local != nil {
err := local.Close()
if err!=nil{
fmt.Println("close local:" + err.Error())
}
}
if remote != nil {
err := remote.Close()
if err!=nil{
fmt.Println("close remote:" + err.Error())
}
我们经常会遇到一个问题,如何将本机的服务暴露到公网上,让别人也可以访问。我们知道,在家上网的时候我们有一个 IP 地址,但是这个 IP 地址并不是一个公网的 IP 地址,别人无法通过一个 IP 地址访问到你的服务,所以在例如:微信接口调试、三方对接的时候,你必须将你的服务部署到一个公网的系统中去,这样太累了。 这个时候,内网穿透就出现了,它的作用就是即使你在家的服务,也能被其人访问到。 今天让我们来用一个最简单的案例学习一下如何用 go 来做一个最简单的内网穿透工具。