整合营销服务商

电脑端+手机端+微信端=数据同步管理

免费咨询热线:

PHP 获取网页所有链接

PHP 获取网页所有链接

下代码用于采集页面时,获取网页中所有的链接,并循环输出:

$html=file_get_contents('http://www.runoob.com');

$dom=new DOMDocument();

@$dom->loadHTML($html);

// grab all the on the page

$xpath=new DOMXPath($dom);

$hrefs=$xpath->evaluate("/html/body//a");

for ($i=0; $i < $hrefs->length; $i++) {

$href=$hrefs->item($i);

$url=$href->getAttribute('href');

echo $url.'<br />';

}


.转换工具:PDF转HTML

1.本地及在线软件:

Abbyy FineReader/万兴PDF阅读器支持直接导出HTML,Caliber则可转换PDF至Zip格式再解压

备选:
1.小于40M的PDF文件用Word直接导出成网页 2.PDF阅读器如福昕阅读器可先转Word后再导出网页. 总之,如PDF阅读器不能直转html,可转Word/Epub/Zip再通过解压或者再次导出.
可以使用在线的转换软件,但可能会收费或有各种限制及不稳定,我在文中有推荐了部分网址.

2.定制开发工具包:

  • mutool 工具 : 免费开源PDF批处理工具,通过CMD命令来转换PDF至文本/图片/HTML网页

https://mupdf.com/index.htmlmupdf.com/index.html

https://github.com/pymupdfgithub.com/pymupdf

  • poppler 命令行工具及对应的python开发包组件,也提供了命令行工具集,支持图片单独保存,复杂模式-C会导出一个大纲,在我体验时出现部分格式问题,所以可以酌情参考一下再取舍.

https://poppler.freedesktop.org/poppler.freedesktop.org/

  • pdf2htmlex工具: 基于mupdf / poppler基础上改进,生成网页效果好,但是DOM结构比较复杂,浏览器解析时较费资源,不太适合导入SM

https://github.com/coolwanglu/pdf2htmlEXgithub.com/coolwanglu/pdf2htmlEX

说明:pdf2htmlex生成的网页中图片也是base64编码保存的,所以SM中导入后也不显示图片


二. 操作流程: (使用mutool来作演示)

1.把PDF转换成HTML网页 2.导入SuperMemo学习

首选Abbyy FineReader/万兴PDF阅读器/Mutool命令行/Word应用程序(PDF小于40M)来转换,本文我们使用Mutool演示. 除使用Word格式(PDF小于40M直接用WORD打开,否则用福昕阅读器先转Word格式再用Word导出HTML).还可使用Epub或Zip格式(万兴等PDF阅读器先转至Epub或Caliber先转至Zip再用解压工具解压. 注:Caliber貌似用的是pdf2htmlEX项目来转换).


1.转换PDF至HTML

mutool在之前文章已经作了介绍,本文不再进行安装设置说明,你可以参考下文进行下载及配置

一只小胖子:实践-SuperMemo-PDF文档图片增量4 赞同 · 3 评论文章

你也还可以参考官方的文档:

MuPDF Documentationmupdf.com/docs/index.html

步骤一: 查看mutool 工具的转换参数(这里用的是convert命令,其实还可以用draw命令实现)

查看mutool的用法可一起使用的参数

draw命令有大部分功能和convert命令是重合的,所以它的使用参数和convert命令基本一致. 如下为draw命令日常使用的中文说明,如需更具体参考你可以看我上面放置的官网文档链接:

draw命令也可实现

MuPDF Command Line Toolswww.jianshu.com/p/a4b6a699ce17

mutool man page - mupdf - General Commandswww.mankier.com/1/mutool#


步骤二: 执行如下转换命令(注意:pdf文件后是对应的PDF页码,如果不输入默认转换所有页):

mutool convert -F html -o myfile.html -O preserve-images Y:OneDrivePDF书籍轻松Scrum之旅.pdf 1,3,44,5-20

cmd窗口执行命令

如下正确生成了html文档,浏览器打开如下:

转换带图片的网页成功

步骤三: 用IE打开网页,Ctrl+Shift+A 导入网页并进行学习

网页可直接导入SM进行学习

上面生成的网页中图片默认是用base64编码展示的,SM软件不支持这些图片的导入,所以增量阅读时图片区域会显示出一个叉号,所以我建议在增量阅读到这个图片时,如果不需要这个图片则可以直接删除这个叉号,如果需要则可以直接从网页或PDF源文件中复制或截图过来,如下图:

SM软件不支持base64图片导入手动修复部分图片的问题

以上我是用mutool工具来实现PDF至网页生成并学习的具体步骤,因该命令行工具现版本所支持的html导出用的是base64编码来存储图片,所以如上我会增加一步手工取图的操作(可以用简单的脚本处理下网页,把base64图片文件化,然后替换图片链接,具体可以参考我文末的方案).

简单较小的PDF文件,Word程序来导出也可以,不然则可用其它备选工具,如Abbyy FineReader或万兴PDF阅读器(PDF转换成HTML或转成Epub再解压)来实现PDF到HTML网页的转换,这两个工具生成的网页图片是可以直接导入的,但注意,这两个软件也不是完美的. 比如: 软件收费/文本识别率有时会比mutool更低 / 排版错乱 /转换速度慢/转换时消耗资源高/不方便需求定制等.

====总结: PDF转换至HTML网页的方案===

1.ABBYY FineReader / 万兴PDF阅读器 软件本身支持转换文本图形分离的网页,只不过要收费,另外也可以直接Word程序(PDF大小限制),福昕->Word,万兴->Epub,Caliber->Zip等间接方式.

2.在线的转换工具,WPS在线转换/永中在线转换等在线转换工具,会有各种限制及收费,不稳定等问题, 也存在一定隐私问题,无法自定规则及不方便批处理.如下放置几个免费的在线转换网址:

https://www.pdf-online.com/osa/extract.aspx?o=imgwww.pdf-online.com/osa/extract.aspx?o=img

https://www.pdftohtml.netwww.pdftohtml.net

ConvertFiles.comConvertFiles.com


3.mutool 命令行工具及 mupdf api 库 / poppler / 基于前两个软件的pdf2htmlEX 等转换工具

mutool使用简单,只不过生成网页中图片是用base64编码的,上面我建议是这样操作: 导入进SM软件来学习时,显示叉号但不需要的图片可直接删除,需要的图片直接从网页拷贝源图片到SM软件即可.如果你一定要导入图片在SM学习可以按我文末的方法简单处理一下生成的HTML网页.

poppler自带了一套工具集,可以实现PDF转换成文本/图片/网页. 使用工具pdf2html可以生成文本及图片分离的网页,但实验中会有一小部分网页格式上会有乱的情况,所以自己综合看下效果.

pdf2htmlEX 生成网页效果最好,但网页文件较大/DOM结构较复杂/网页解析消耗资源,图片也是返回base64,如果是要导入SM中进行增量,还是不建议了,直接用浏览器打开体验还是很不错的.


Mutool免费开源,提供了命令行工具套件及pymupdf编程包,社区活跃,迭代的速度很快.支持多线程,网页转换速度相当快.方便自己定制处理,所以比较推荐.

如下: 我们会发现mupdf官网版本修复/更新非常快,很多软件现在没有的功能相信很快会出现:

代码提交更新数据

--------2021-01-31 增加如下--------:

针对mutool导出html时图片默认为base64编码的问题.网上也有人提出过问题,官方回答是建议自己单独写代码来处理一下,把base64的图片保存至本地并用本地链接替换回原来的img链接.

mutool-save-images-as-independent-files-manage-paragraphscomp.lang.postscript.narkive.com/N1Bes6uK/mutool-save-images-as-independent-files-manage-paragraphs

https://github.com/pymupdf/PyMuPDF/issues/154github.com/pymupdf/PyMuPDF/issues/154

# 方式一 : 对mutool生成的HTML网页直接解析,解码出图片并替换链接...
image = json.loads(pg.getText('json'))['blocks'][0]['image']  
img = Image.open(io.BytesIO(base64.b64decode(image))) 
#fitz.Pixmap(doc, xref)

Collection of Recipespymupdf.readthedocs.io/en/latest/faq.html#how-to-extract-images-non-pdf-documents

Appendix 2: Details on Text Extractiondocumentation.help/PyMuPDF/app2.html

# 方式二: 直接使用Mupdf的API自已生成HTML,使用dict来取图片及文本...doc = fitz.open(file_path)for page in doc:
  page.getText("dict")

我是一只热爱学习的小胖子,如果你也热爱学习,并且对SuperMemo感兴趣,欢迎转发和评论!

. 增强HttpServletResponse对象

1. 实现一个增强的HttpServletResponse类,需要继承javax.servlet.http.HttpServletRequestWrapper类,通过重写自己需要增强的方法来实现(这种模式就叫做装饰者模式),使用该增强类在加上过滤器就可以实现无编码转换处理代码。

public class MyRequest extends HttpServletRequestWrapper{
	private HttpServletRequest req;
	public MyRequest(HttpServletRequest request) {
		super(request);
		req=request;
	}
	@Override
	public String getParameter(String name) {
		//解决编码问题,无论是post还是get请求,都不需要在业务代码中对编码再处理
		String method=req.getMethod();
		if("get".equalsIgnoreCase(method)){
			try {
				String str=req.getParameter(name);
				byte[] b=str.getBytes("iso8859-1");
				String newStr=new String(b, "utf-8");
				return newStr;
			} catch (UnsupportedEncodingException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}else if("post".equalsIgnoreCase(method)){
			try {
				req.setCharacterEncoding("utf-8");
			} catch (UnsupportedEncodingException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		//绝对不能删除此行代码,因为此行代码返回的就是编码之后的数据
		return super.getParameter(name);
	}
}

在过滤器中应用

public class FilterTest4 implements Filter {
	@Override
	public void init(FilterConfig filterConfig) throws ServletException {}
	@Override
	public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
			throws IOException, ServletException {
		//生成增强的HttpServletRequest对象
		HttpServletRequest req=(HttpServletRequest) request;
		MyRequest myReq=new MyRequest(req);
		//将增强的HttpServletRequest对象传入过滤器执行链中,在后面传入的request对象都会是增强的HttpServletRequest对象
		chain.doFilter(myReq, response);
		
	}
	@Override
	public void destroy() {}
}

2. 文件上传原理过程

1. JavaWeb中实现文件上传:

  • 客户端:HTML页面需要一个<form>表单,且必须设置表单的enctype属性值为"multipart/form-data",以及method属性值为"post"(因为get方式不支持大量数据提交);表单里有一个<input type="file" name="">的标签,且name属性值必须指定。
<html>
 <head>
 <title>My JSP 'upload.jsp' starting page</title>
	<meta http-equiv="pragma" content="no-cache">
	<meta http-equiv="cache-control" content="no-cache">
	<meta http-equiv="expires" content="0"> 
	<meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
	<meta http-equiv="description" content="This is my page">
 </head>
 
 <body>
 <form action="" method="post" enctype="multipart/form-data">
 <input type="text" name="name">
 	请选择文件:<input type="file" name="upload">
 	<input type="submit" value="上传">
 </form>
 </body>
</html>
  • 服务端:主要进行IO读写操作。必须导入commons-fileupload和commons-io两个jar包,可以通过请求request对象的getInputStream获得一个流来读取请求中的文件数据,但是如果客户端上传多个文件时,就会很麻烦,所以提供了commons-fileupload和commons-io两个jar包来更方便的实现文件上传。
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.List;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileUploadException;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
public class UploadServlet extends HttpServlet{
	@Override
	protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		/**
		 * 1. 创建磁盘文件项工厂类 DiskFileItemFactory
		 * 2. 创建核心解析Request类 ServletFileUpload
		 * 3. 开始解析Request对象中的数据,并返回一个List集合
		 * 4. List中包含表单中提交的内容
		 * 5. 遍历集合,获取内容
		 */
		DiskFileItemFactory fac=new DiskFileItemFactory();
		
		ServletFileUpload upload=new ServletFileUpload(fac);
		upload.setHeaderEncoding("utf-8");//防止中文的文件名乱码
		try {
			List<FileItem> fileItems=upload.parseRequest(req);
			for(FileItem item:fileItems){
				//有可能是普通文本项,比如<input type="text">标签提交上来的字符串
				//也有可能是<input type="submit" value="上传">上传的文件
				//文件项与普通项有不同的API来处理
				//首先判断是普通文本项还是文件项,
				if(item.isFormField()){
					//true表示普通文本项
					//获取文本项的name属性值
					String name=item.getFieldName();
					//获取对应的文本
					String value=item.getString("utf-8");//防止中文乱码
					System.out.println(name+":"+value);
				}else{
					//false表示文件项
					//先获取文件名称
					String name=item.getName();
					//获取文件项的输入流
					InputStream in=item.getInputStream();
					//获取服务器端文件存储的目标磁盘路径
					String path=getServletContext().getRealPath("/upload");
					System.out.println(path);
					//获取输出流,输出到本地文件中
					OutputStream out=new FileOutputStream(path+"/"+name);
					//写入数据
					int len=0;
					byte[] b=new byte[1024];
					while((len=in.read(b))!=-1){
						out.write(b,0,len);
					}
					in.close();
					out.close();
					
				}
			}
		} catch (FileUploadException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}

注意:在文件上传时,会将form表单的属性enctype属性值为"multipart/form-data",当提交到服务端后,无法使用 req.getParameter(name) 方法来获取到内容,只有通过上面的方法来获取文本项。

2. 文件上传相关核心类:

  • DiskFileItemFactory:相关API如下
  • public DiskFileItemFactory():无参构造器
  • public DiskFileItemFactory(int sizeThreshold, File repository):构造器,sizeThreshold设置缓冲区大小,默认10240 byte;repository表示如果过缓冲区空间小于上传文件空间,那么会生成临时文件,repository就是指定该临时文件的保存路径,如果过未上传完成就中断,继续上传时就可以通过这个临时文件来继续上传。
  • public void setSizeThreshold(int sizeThreshold):设置缓冲区大小
  • public void setRepository(File repository):指定该临时文件的保存路径
//改进上面的文件上传代码,添加一个临时文件
public class UploadServlet extends HttpServlet{
	@Override
	protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		DiskFileItemFactory fac=new DiskFileItemFactory();
		fac.setSizeThreshold(1024*1024);//设置缓冲区为1mb
		//设置临时文件的本地磁盘存储路径
		File repository=new File(getServletContext().getRealPath("/temp"));
		fac.setRepository(repository);
		ServletFileUpload upload=new ServletFileUpload(fac);
		upload.setHeaderEncoding("utf-8");//防止中文的文件名乱码
		try {
			List<FileItem> fileItems=upload.parseRequest(req);
			for(FileItem item:fileItems){
				if(item.isFormField()){
					String name=item.getFieldName();
					String value=item.getString();
					String value=item.getString("utf-8");//防止中文乱码
					System.out.println(name+":"+value);
				}else{
					String name=item.getName();
					InputStream in=item.getInputStream();
					String path=getServletContext().getRealPath("/upload");
					System.out.println(path);
					OutputStream out=new FileOutputStream(path+"/"+name);
					int len=0;
					byte[] b=new byte[1024];
					while((len=in.read(b))!=-1){
						out.write(b,0,len);
					}
					in.close();
					out.close();
					
				}
			}
		} catch (FileUploadException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}
  • ServletFileUpload:相关API如下
  • public static final boolean isMultipartContent( HttpServletRequest request) :判断表单提交上来的数据内容是否是multipart类型的数据,即 form表单的 enctype="multipart/form-data",是则返回true,否则返回false
  • public List /* FileItem */ parseRequest(HttpServletRequest request):解析request对象,返回一个泛型为FileItem 的List集合
  • public void setFileSizeMax(long fileSizeMax):设置单个文件的空间大小的最大值
  • public void setSizeMax(long sizeMax):设置所有文件空间大小之和的最大值
  • public void setHeaderEncoding(String encoding):解决上传文件名的乱码问题
  • public void setProgressListener(ProgressListener pListener):上传时的进度条。
  • FileItem:封装表单中提交的数据
  • boolean isFormField():判断当前FileItem对象是表单中提交的文本数据项,还是文件数据项
  • String getFieldName():获取文本项类型FileItem对象的name属性值,即相当于表单中的 <input type="text" name="name">
  • String getString( String encoding ):获取文本项类型FileItem对象的value值,可以指定编码格式,也可以省略encoding不写
  • String getName():应用于文件项类型的FileItem对象,用于获取文件的名称,包括后缀名
  • InputStream getInputStream():获取输入流
  • void delete():删除临时缓存文件(在输入以及输出流关闭后执行)

3. 实现多文件上传(需要js技术):主要是更改jsp页面,通过js代码来添加多个文件进行上传,服务器代码无需更改

<%@ page language="java" import="java.util.*" pageEncoding="UTF-8" contentType="text/html; charset=utf-8"%>
<%
String path=request.getContextPath();
String basePath=request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
 <head>
 <base href="<%=basePath%>">
 <title>My JSP 'upload.jsp' starting page</title>
	<meta http-equiv="pragma" content="no-cache">
	<meta http-equiv="cache-control" content="no-cache">
	<meta http-equiv="expires" content="0"> 
	<meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
	<meta http-equiv="description" content="This is my page">
 </head>
 
 <body>
 <script type="text/javascript">
 	function run(){
 		var div=document.getElementById("divId");
 		 div.innerHTML+="<div><input type='file' name='upload'><input type='button' value='删除' onclick='del(this)'></div>"
 	}
 	function del(presentNode){
 		var div=document.getElementById("divId");
 		div.removeChild(presentNode.parentNode);
 	}
 </script>
 <div>
 	多文件上传<br/>
 <form action="/Servlet/upload" method="post" enctype="multipart/form-data">
 	<input type="button" value="添加" onclick="run()"><br/>
 	<div id="divId">
 	
 	</div>
 	
 	<input type="submit" value="上传">
 </form>
 </div>
 </body>
</html>

4. 关于文件上传的一些问题:

  • 文件重名可能会产生覆盖效果,可以在处理文件名时生成一个唯一的文件名
  • 如果所有的文件都储存在一个文件夹中,会导致文件夹下文件过多,可以一个用户一个文件夹,或者利用算法目录分离等方法。

3. 文件下载

1. 传统文件下载方式有超链接下载或者后台程序下载两种方式。通过超链接下载时,如果浏览器可以解析,那么就会直接打开,如果不能解析,就会弹出下载框;而后台程序下载就必须通过两个响应头和一个文件的输入流。

2. 后台程序下载:

  • 两个响应头:
  • Content-Type:其值有比如 text/html;charset=utf-8
  • Content-Disposition:其值为 attachment;filename=文件名 以附件形式打开
  • 流:需要获取文件的输入流,然后通过response对象的输出流输出到客户端。