Java、C#双语版HttpHelper类
Java、C#双语版HttpHelper类(解决网页抓取乱码问题)
在做一些需要抓取网页的项目时,经常性的遇到乱码问题。最省事的做法是去需要抓取的网站看看具体是什么编码,然后采用正确的编码进行解码就OK了,不过总是一个个页面亲自去判断也不是个事儿,尤其是你需要大量抓取不同站点的页面时,比如网页爬虫类的程序,这时我们需要做一个相对比较通用的程序,进行页面编码的正确识别。
乱码问题基本上都是编码不一致导致的,比如网页编码使用的是UTF-8,你使用GB2312去读取,肯定会乱码。知道了本质问题后剩下的就是如何判断网页编码了。GBK、GB2312、UTF-8、BIG-5,一般来说遇到的中文网页编码大多是这几种,简化下就是只有 GBK和UTF-8两种,不夸张的说,现在的网站要么是GBK编码,要么是UTF-8编码,所以接下来的问题就是判断站点具体是UTF-8的还是GBK的。
那怎么判断页面具体编码呢?首先查看响应头的 Content-Type,若响应头里找不到,再去网页里查找meta头,若还是找不到,那没办法了,设置个默认编码吧,个人推荐设置成UTF-8。比如访问博客园首页http://www.cnblogs.com/,可以在响应头里看到 Content-Type: text/html; charset=utf-8,这样我们就知道博客园是采用utf-8编码,但并不是所有的网站都会在响应头Content-Type加上页面编码,比如百度的就是Content-Type: text/html,找不到charset,这时只能去网页里面找<meta http-equiv=Content-Type content="text/html;charset=utf-8">,确认网页最终编码,总结下就是下面几步
- 1.响应头查找Content-Type中的charset,若找到了charset则跳过步骤2,3,直接进行第4步
- 2.若步骤1得不到charset,则先读取网页内容,解析meta里面的charset得到页面编码
- 3.若步骤2种还是没有得到页面编码,那没办法了设置默认编码为UTF-8
- 4.使用得到的charset重新读取响应流
通过上面方法基本上能正确解析绝大多数页面,实在不能识别的只好亲自去核实下具体编码了
注意:
- 1.现在站点几乎都启用了gzip压缩支持,所以在请求头里面加上Accept-Encoding:gzip,deflate,这样站点会返回压缩流,能显著的提高请求效率
- 2.由于网络流不支持流查找操作,也就是只能读取一次,为了提高效率,所以这里建议将http响应流先读取到内存中,以方便进行二次解码,没有必要重新请求去重新获取响应流
下面分别给出Java和C#版的实现代码,页面底部给出了源码的git链接,有需要的童鞋请自行下载
Java实现

package com.cnblogs.lzrabbit.util; import java.io.*;
import java.net.*;
import java.util.*;
import java.util.Map.Entry;
import java.util.regex.*;
import java.util.zip.*; public class HttpUtil { public static String sendGet(String url) throws Exception {
return send(url, "GET", null, null);
} public static String sendPost(String url, String param) throws Exception {
return send(url, "POST", param, null);
} public static String send(String url, String method, String param, Map<String, String> headers) throws Exception {
String result = null;
HttpURLConnection conn = getConnection(url, method, param, headers);
String charset = conn.getHeaderField("Content-Type");
charset = detectCharset(charset);
InputStream input = getInputStream(conn);
ByteArrayOutputStream output = new ByteArrayOutputStream();
int count;
byte[] buffer = new byte[4096];
while ((count = input.read(buffer, 0, buffer.length)) > 0) {
output.write(buffer, 0, count);
}
input.close();
// 若已通过请求头得到charset,则不需要去html里面继续查找
if (charset == null || charset.equals("")) {
charset = detectCharset(output.toString());
// 若在html里面还是未找到charset,则设置默认编码为utf-8
if (charset == null || charset.equals("")) {
charset = "utf-8";
}
} result = output.toString(charset);
output.close(); // result = output.toString(charset);
// BufferedReader bufferReader = new BufferedReader(new
// InputStreamReader(input, charset));
// String line;
// while ((line = bufferReader.readLine()) != null) {
// if (result == null)
// bufferReader.mark(1);
// result += line;
// }
// bufferReader.close(); return result;
} private static String detectCharset(String input) {
Pattern pattern = Pattern.compile("charset=\"?([\\w\\d-]+)\"?;?", Pattern.CASE_INSENSITIVE);
if (input != null && !input.equals("")) {
Matcher matcher = pattern.matcher(input);
if (matcher.find()) {
return matcher.group(1);
}
}
return null;
} private static InputStream getInputStream(HttpURLConnection conn) throws Exception {
String ContentEncoding = conn.getHeaderField("Content-Encoding");
if (ContentEncoding != null) {
ContentEncoding = ContentEncoding.toLowerCase();
if (ContentEncoding.indexOf("gzip") != 1)
return new GZIPInputStream(conn.getInputStream());
else if (ContentEncoding.indexOf("deflate") != 1)
return new DeflaterInputStream(conn.getInputStream());
} return conn.getInputStream();
} static HttpURLConnection getConnection(String url, String method, String param, Map<String, String> header) throws Exception {
HttpURLConnection conn = (HttpURLConnection) (new URL(url)).openConnection();
conn.setRequestMethod(method); // 设置通用的请求属性
conn.setRequestProperty("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8");
conn.setRequestProperty("Connection", "keep-alive");
conn.setRequestProperty("User-Agent", "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/33.0.1750.117 Safari/537.36");
conn.setRequestProperty("Accept-Encoding", "gzip,deflate"); String ContentEncoding = null;
if (header != null) {
for (Entry<String, String> entry : header.entrySet()) {
if (entry.getKey().equalsIgnoreCase("Content-Encoding"))
ContentEncoding = entry.getValue();
conn.setRequestProperty(entry.getKey(), entry.getValue());
}
} if (method == "POST") {
conn.setDoOutput(true);
conn.setDoInput(true);
if (param != null && !param.equals("")) {
OutputStream output = conn.getOutputStream();
if (ContentEncoding != null) {
if (ContentEncoding.indexOf("gzip") > 0) {
output=new GZIPOutputStream(output);
}
else if(ContentEncoding.indexOf("deflate") > 0) {
output=new DeflaterOutputStream(output);
}
}
output.write(param.getBytes());
}
}
// 建立实际的连接
conn.connect();
return conn;
}
}

C#实现

using System;
using System.Collections;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Security;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Text.RegularExpressions;
using System.Web;
using System.IO.Compression;
using System.Collections.Generic;
using System.Collections.Specialized; namespace CSharp.Util.Net
{
public class HttpHelper
{
private static bool RemoteCertificateValidate(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors errors)
{
//用户https请求
return true; //总是接受
} public static string SendPost(string url, string data)
{
return Send(url, "POST", null, null);
} public static string SendGet(string url)
{
return Send(url, "GET", null, null);
} public static string Send(string url, string method, string data, HttpConfig config)
{
if (config == null) config = new HttpConfig();
string result;
using (HttpWebResponse response = GetResponse(url, method, data, config))
{
Stream stream = response.GetResponseStream(); if (!String.IsNullOrEmpty(response.ContentEncoding))
{
if (response.ContentEncoding.Contains("gzip"))
{
stream = new GZipStream(stream, CompressionMode.Decompress);
}
else if (response.ContentEncoding.Contains("deflate"))
{
stream = new DeflateStream(stream, CompressionMode.Decompress);
}
} byte[] bytes = null;
using (MemoryStream ms = new MemoryStream())
{
int count;
byte[] buffer = new byte[4096];
while ((count = stream.Read(buffer, 0, buffer.Length)) > 0)
{
ms.Write(buffer, 0, count);
}
bytes = ms.ToArray();
} #region 检测流编码
Encoding encoding; //检测响应头是否返回了编码类型,若返回了编码类型则使用返回的编码
//注:有时响应头没有编码类型,CharacterSet经常设置为ISO-8859-1
if (!string.IsNullOrEmpty(response.CharacterSet) && response.CharacterSet.ToUpper() != "ISO-8859-1")
{
encoding = Encoding.GetEncoding(response.CharacterSet == "utf8" ? "utf-8" : response.CharacterSet);
}
else
{
//若没有在响应头找到编码,则去html找meta头的charset
result = Encoding.Default.GetString(bytes);
//在返回的html里使用正则匹配页面编码
Match match = Regex.Match(result, @"<meta.*charset=""?([\w-]+)""?.*>", RegexOptions.IgnoreCase);
if (match.Success)
{
encoding = Encoding.GetEncoding(match.Groups[1].Value);
}
else
{
//若html里面也找不到编码,默认使用utf-8
encoding = Encoding.GetEncoding(config.CharacterSet);
}
}
#endregion result = encoding.GetString(bytes);
}
return result;
} private static HttpWebResponse GetResponse(string url, string method, string data, HttpConfig config)
{
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
request.Method = method;
request.Referer = config.Referer;
//有些页面不设置用户代理信息则会抓取不到内容
request.UserAgent = config.UserAgent;
request.Timeout = config.Timeout;
request.Accept = config.Accept;
request.Headers.Set("Accept-Encoding", config.AcceptEncoding);
request.ContentType = config.ContentType;
request.KeepAlive = config.KeepAlive; if (url.ToLower().StartsWith("https"))
{
//这里加入解决生产环境访问https的问题--Could not establish trust relationship for the SSL/TLS secure channel
ServicePointManager.ServerCertificateValidationCallback = new RemoteCertificateValidationCallback(RemoteCertificateValidate);
} if (method.ToUpper() == "POST")
{
if (!string.IsNullOrEmpty(data))
{
byte[] bytes = Encoding.UTF8.GetBytes(data); if (config.GZipCompress)
{
using (MemoryStream stream = new MemoryStream())
{
using (GZipStream gZipStream = new GZipStream(stream, CompressionMode.Compress))
{
gZipStream.Write(bytes, 0, bytes.Length);
}
bytes = stream.ToArray();
}
} request.ContentLength = bytes.Length;
request.GetRequestStream().Write(bytes, 0, bytes.Length);
}
else
{
request.ContentLength = 0;
}
} return (HttpWebResponse)request.GetResponse();
}
} public class HttpConfig
{
public string Referer { get; set; } /// <summary>
/// 默认(text/html)
/// </summary>
public string ContentType { get; set; } public string Accept { get; set; } public string AcceptEncoding { get; set; } /// <summary>
/// 超时时间(毫秒)默认100000
/// </summary>
public int Timeout { get; set; } public string UserAgent { get; set; } /// <summary>
/// POST请求时,数据是否进行gzip压缩
/// </summary>
public bool GZipCompress { get; set; } public bool KeepAlive { get; set; } public string CharacterSet { get; set; } public HttpConfig()
{
this.Timeout = 100000;
this.ContentType = "text/html; charset=" + Encoding.UTF8.WebName;
this.UserAgent = "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/33.0.1750.117 Safari/537.36";
this.Accept = "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8";
this.AcceptEncoding = "gzip,deflate";
this.GZipCompress = false;
this.KeepAlive = true;
this.CharacterSet = "UTF-8";
}
}
}

注:此文章属懒惰的肥兔原创,
Java、C#双语版HttpHelper类的更多相关文章
- Java、C#双语版HttpHelper类(解决网页抓取乱码问题)
在做一些需要抓取网页的项目时,经常性的遇到乱码问题.最省事的做法是去需要抓取的网站看看具体是什么编码,然后采用正确的编码进行解码就OK了,不过总是一个个页面亲自去判断也不是个事儿,尤其是你需要大量抓取 ...
- [Python+Java双语版自动化测试(接口测试+Web+App+性能+CICD)
[Python+Java双语版自动化测试(接口测试+Web+App+性能+CICD)开学典礼](https://ke.qq.com/course/453802)**测试交流群:549376944**0 ...
- [C#HttpHelper]类1.4正式版教程与升级报告
[C#HttpHelper]类1.4正式版教程与升级报告 导读 1.升级报告 2.HttpHelper1.4正式版下载 3.HttpHelper类使用方法, 4.最简单的Post与Get的写法 ...
- Java、C#双语版配套AES加解密示例
这年头找个正经能用的东西那是真难,网上一搜索一大堆,正经能用的没几个,得,最后还是得靠自己,正巧遇上需要AES加解密的地方了,而且还是Java和C#间的相互加解密操作,这里做个备忘 这里采用的加解 ...
- 转载:Java、C#双语版配套AES加解密示例
转载,原文出处 http://www.cnblogs.com/lzrabbit/p/3639503.html 这年头找个正经能用的东西那是真难,网上一搜索一大堆,正经能用的没几个,得,最后还是得靠自己 ...
- java中常用的工具类(一)
我们java程序员在开发项目的是常常会用到一些工具类.今天我汇总了一下java中常用的工具方法.大家可以在项目中使用.可以收藏!加入IT江湖官方群:383126909 我们一起成长 一.String工 ...
- 《Thinking In Java第四版》拾遗
<Thinking In Java第四版>拾遗 转自我的github(http://katsurakkkk.github.io/2016/05/Thinking-In-Java%E7%AC ...
- 工厂方法模式(Java与Kotlin版)
前文推送 设计模式 简单工厂模式(Java与Kotlin版) Kotlin基础知识 Kotlin入门第一课:从对比Java开始 Kotlin入门第二课:集合操作 Kotlin入门第三课:数据类型 初次 ...
- 不错的 HttpHelper类 c#
/// <summary>/// 类说明:HttpHelper类,用来实现Http访问,Post或者Get方式的,直接访问,带Cookie的,带证书的等方式,可以设置代理/// 重要提示: ...
随机推荐
- 内存四个领域,变量声明和定义,注册,c内联汇编,auto,堆,不变,静态变量
1.内存四大区域 2.在程序中,变量的声明能够有多份,定义仅仅能有一份 watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvdG90b3R1enVvcXVh ...
- Maven+struts2+spring4+hibernate4的环境搭建
搭建Maven+struts2+spring4+hibernate4其实并不难!但开始弄的时候还是费了我好大的力气,老是出现这样那样的错误!好了,废话不多说,开始搭建开发环境. 一.Myeclipse ...
- 微信公众号PHP简单开发流程
原文:微信公众号PHP简单开发流程 微信公众号开发分傻瓜模式和开发者模式两种,前者不要考虑调用某些接口,只要根据后台提示傻瓜式操作即可,适用于非专业开发人员. 开发模式当然就是懂程序开发的人员使用的. ...
- cocos2d-x-3.1 经常使用宏 (coco2d-x 学习笔记五)
在代码中使用这些宏,能够降低敲键盘的次数,从而提高编写效率. 与节点属性(property)相关的 CC_PROPERTY_READONLY CC_PROPERTY_READONLY_PASS_BY_ ...
- Effective C++ 18-23
18.接口用于完整的类,使最小. 用户接口类是指程序猿这个类可以访问所获得的接口,典型接口具有在存在唯一功能,好的包装类的数据成员. 这意味着一个完整的接口,包括所有 合理的功能操作.最小指功能和特征 ...
- 【转】Android 常用 adb 命令总结
原文地址:http://testerhome.com/topics/2565 针对移动端 Android 的测试, adb 命令是很重要的一个点,必须将常用的 adb 命令熟记于心, 将会为 Andr ...
- java学习笔记1——window7下JDK环境变量配置图解
1. 首先下载Java安装工具包 http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.ht ...
- view components介绍
view components介绍 在ASP.NET MVC 6中,view components (VCs) 功能类似于虚拟视图,但是功能更加强大. VCs兼顾了视图和控制器的优点,你可以把VCs ...
- 轻型ORM--Dapper
分享一个轻型ORM--Dapper选用理由 推荐理由:Dapper只有一个代码文件,完全开源,你可以放在项目里的任何位置,来实现数据到对象的ORM操作,体积小速度快:) Google Code下载地址 ...
- sql 中如何取出指定行: Row_Number
原文:sql 中如何取出指定行: Row_Number ROW_NUMBER (Transact-SQL) USE AdventureWorks2008R2;GOWITH OrderedOrders ...