c#抓取网页数据(最省事的做法是去需要的网站看看具体是什么编码 )
优采云 发布时间: 2022-01-05 22:11c#抓取网页数据(最省事的做法是去需要的网站看看具体是什么编码
)
在做一些需要抓取网页的项目时,经常会遇到乱码。最简单的方法就是去需要爬取的网站看看具体的编码是什么,然后使用正确的编码进行解码。可以,但是总是一一判断也不是问题,尤其是当你需要从不同站点抓取大量页面的时候,比如一个网络爬虫程序,那么我们就需要做一个比较通用的程序来正确识别页面编码。
乱码问题基本上是编码不一致造成的。比如网页编码使用UTF-8,如果使用GB2312读取,肯定会出现乱码。知道了本质问题之后,剩下的就是如何判断网页编码了。GBK、GB2312、UTF-8、BIG-5,一般来说,遇到的中文网页编码大多是这几种编码。简单来说,只有GBK和UTF-8两种,一点都不夸张。,目前的网站要么是GBK编码,要么是UTF-8编码,所以接下来的问题就是确定网站具体是UTF-8还是GBK。
如何确定页面的具体编码?首先检查响应头的Content-Type。如果在响应头中找不到,请到网页中查找元头。如果还是找不到,那就没办法了。设置默认编码。个人建议设置为UTF-8。比如访问博客园的首页,可以看到Content-Type:text/html;响应头中的charset=utf-8,所以我们知道博客园使用的是utf-8编码,但是并不是所有的网站都会在响应头的Content-Type中添加页面编码。比如百度的就是Content-Type:text/html,查不到charset。这时候只能在网页中找到它来确认网页的最终编码。总结就是以下步骤
1.在响应头中的 Content-Type 中找到字符集。如果找到charset,则跳过第2步和第3步,直接进入第4步。2.如果第1步没有找到charset,先读取网页的Content,解析meta中的charset得到页面编码< @3.如果在第2步还是没有获取到页面编码,则没有办法设置默认编码为UTF-84. 使用获取到的charset重新读取响应流
基本上,通过上述方法大部分页面都可以正确解析,但无法识别的则需要亲自验证具体代码。
注意:
1.几乎所有站点都启用了gzip压缩支持,所以在请求头中添加Accept-Encoding:gzip,deflate,这样站点会返回一个压缩流,可以显着提高请求的效率。2.由于网络流不支持流搜索操作,即只能读取一次。为了提高效率,建议先将http响应流读入内存,方便二次解码。无需重新请求检索响应流。
下面给出了Java和C#的实现代码,页面底部给出了源码的git链接。需要童鞋的请自行下载
Java实现
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", data, 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, @"", 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; }
///
/// 默认(text/html)
///
public string ContentType { get; set; }
public string Accept { get; set; }
public string AcceptEncoding { get; set; }
///
/// 超时时间(毫秒)默认100000
///
public int Timeout { get; set; }
public string UserAgent { get; set; }
///
/// POST请求时,数据是否进行gzip压缩
///
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";
}
}
}