我们经常浏览网页,有时候看到一些精美的图片,想下载下来保存,但是太多,如果一张一张的下载,那太费时了;如果你喜欢看书,看小说,那么浏览小说网站是常有的事,但是有时候我们不能联网,比如农村老家,如果还想看,我们有没有想过一次性保存到手机里。网站上的小说都是一章一个页面,难道要我们一次一个章节复制粘贴保存?这个事我真的干了,为了一本小说,我连续花了三个小时,目不转睛地盯着电脑复制粘贴。还有抓取别人的网站一些有用数据,为我们服务,比如说找工作的事,买彩票的事等等。

现在我要来实现一个下载他趣网站上性感漂亮女性图片,发现这个网站也是通过别人博客得知的。那些图片很不错,养眼,所以对这个网站感兴趣了,也就有动力了。

今天写这个博客就是为了记录一次学习爬虫过程,分享自己遇到一些问题,以及解决问题的方法经验。

常规思路是,首先打开我们要爬取的网站,然后点击审查网页元素,分析HTML结构如下图

然后就开始写代码,初看这肯定要从HTML元素中获取图片链接了。于是我还下载了Jumony 一个提取网页元素的帮助类。

 url = "http://www.taqu.cn/community/";
HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(url);
using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
{
// 请求成功的状态码:200
if (response.StatusCode == HttpStatusCode.OK)
{
using (Stream stream = response.GetResponseStream())
{
using (StreamReader reader = new StreamReader(stream))
{
string html = reader.ReadToEnd(); var parser = new JumonyParser();
var doc = parser.Parse(html);
var lists = doc.Find("#subject_list > ul > li");
foreach (var li in lists)
{
var img = li.FindFirst("img");
var src =img.Attribute("src").Value();
DownloadImage(src);
}
//Console.WriteLine(html);
}
}
}
else
{
Console.WriteLine("服务器返回错误:{0}", response.StatusCode);
}
}

没有想到string html = reader.ReadToEnd(); 获取的是一张算作空白页,根本就没有我审查元素看到的内容,没有我想要的元素,那要怎么办?难道错了?不行呀,于是我又通过这个观察如下图,有图片的请求,也有内容。——原来是ajax请求呀!

再仔细看,不是有请求么,而且有返回数据

点击他趣girl

返回数据

在Headers这一栏里我们可以发现请求头部一些信息,可以请求之后响应一些信息。主要看请求的。

分析得知,在代码里我们要发送请求需要填写哪些内容并注意事项了。代码如下,参考别人的,后面会说明。

  public static string GetContent(string method, string url, string postData = "", CookieContainer cookie = null)
{
HttpWebResponse response = null;
HttpWebRequest request = null;
if (cookie == null)
cookie = new CookieContainer();
// 准备请求...
try
{
// 设置参数
request = WebRequest.Create(url) as HttpWebRequest;
request.CookieContainer = cookie;
request.AllowAutoRedirect = true;
request.Method = method.ToUpper();
request.ContentType = "application/json, text/javascript, */*; q=0.01";
string userAgent = string.Format("Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36)");//这个根据电脑不同而改变
request.UserAgent = userAgent; if (method.ToUpper() == "POST")
{
request.ContentLength = postData.Length;
if (!string.IsNullOrEmpty(postData))
{
byte[] data = Encoding.Default.GetBytes(postData);
request.ContentLength = data.Length;
using (Stream outstream = request.GetRequestStream())
{
outstream.Write(data, , data.Length);
}
}
}
//发送请求并获取相应回应数据
response = request.GetResponse() as HttpWebResponse;
//直到request.GetResponse()程序才开始向目标网页发送Post请求
using (Stream instream = response.GetResponseStream())
{
using (StreamReader sr = new StreamReader(instream, Encoding.Default))
{ //返回结果网页(html)代码
string content = sr.ReadToEnd();
return UnicodeToGB(content);
}
}
}
catch (Exception ex)
{
string err = ex.Message;
return err;
}
finally
{
if (response != null)
response.Close();
}
}

参考:http://www.cnblogs.com/shoufengwei/p/5654491.html

请求方法是有了,但是返回的数据好像不是汉字哦,是Unicode编码过的数据,于是有了转码这一步

 public static string UnicodeToGB(string text)
{
System.Text.RegularExpressions.MatchCollection mc = System.Text.RegularExpressions.Regex.Matches(text, "\\\\u([\\w]{4})");
if (mc != null && mc.Count > )
{
foreach (System.Text.RegularExpressions.Match m2 in mc)
{
string v = m2.Value;
string word = v.Substring();
byte[] codes = new byte[];
int code = Convert.ToInt32(word.Substring(, ), );
int code2 = Convert.ToInt32(word.Substring(), );
codes[] = (byte)code2;
codes[] = (byte)code;
text = text.Replace(v, Encoding.Unicode.GetString(codes));
}
}
else
{ }
return text;
}

参考:http://www.cnblogs.com/guardianf/archive/2012/08/21/2649147.html

但是,但是数据是json,我要怎么获取其中的图片请求链接呢?我首先想到的事json转datatable 然后循环依次获得。想法没有错,但是怎么写,我又不会。只有网上找了,一搜真的不少,满怀喜悦之情,复制粘贴,运行,试了不少,不是这里报错,就是转化不成功,一下子失望透顶了。怎么办,怎么办。想到网上一些现成方法,应该只适合作者的项目,没有通用性,一下子就轻松多了。再找找看,说不定有的。

这里举一个报错的转化方法

/// <summary>
/// json转换为DataTable
/// </summary>
/// <param name="json">需要转化的json格式字符串</param>
/// <returns></returns>
public DataTable updateInfo(string json)
{
System.Web.Script.Serialization.JavaScriptSerializer jss =new System.Web.Script.Serialization.JavaScriptSerializer();
object[] obj = (object[])jss.DeserializeObject(json);
Dictionary<string, object> dic;
DataRow dr;
DataTable dt = getDataTable();
foreach (object _obj in obj)
{
dr = dt.NewRow();
dt.Rows.Add(dr);
dic = (Dictionary<string, object>)_obj;
dr["id"] = dic["id"];
dr["img_url"] = dic["img_url"];
dr["title"] = dic["title"]; }
return dt;
}

再次改造成功的json转换为DataTable

        /// <summary>
/// json转换为DataTable
/// </summary>
/// <param name="json">需要转化的json格式字符串</param>
/// <returns></returns>
public DataTable updateInfo(string json)
{
System.Web.Script.Serialization.JavaScriptSerializer jss = new System.Web.Script.Serialization.JavaScriptSerializer();
// KeyValuePair<string, object> obj = (KeyValuePair<string, object>)jss.DeserializeObject(json);
var obj = (object)jss.DeserializeObject(json); System.Collections.Generic.Dictionary<string, object> xm = (System.Collections.Generic.Dictionary<string, object>)obj;
DataTable dt = getDataTable();
DataRow dr;
Dictionary<string, object> dic;
object test="";
foreach (var x in xm)
{
if (x.Key == "info")
{
System.Collections.Generic.Dictionary<string, object> xm3 = (System.Collections.Generic.Dictionary<string, object>)x.Value; foreach (var y in xm3)
{
if (y.Key == "data")
{
var xml = (object [])y.Value; foreach (object _obj in xml)
{
dr = dt.NewRow();
dt.Rows.Add(dr);
dic = (Dictionary<string, object>)_obj;
dr["id"] = dic["id"];
dr["img_url"] = dic["img_url"];
dr["title"] = dic["title"]; } }
}
}
}
return dt;
}

经历大半天寻找,实践运行总算有一个转化成功的了。

  #region 将json转换为DataTable
/// <summary>
/// 将json转换为DataTable 参考 http://www.jb51.net/article/61990.htm
/// </summary>
/// <param name="strJson">得到的json</param>
/// <returns></returns>
private DataTable JsonToDataTable(string strJson)
{
//转换json格式
strJson = strJson.Replace(",\"", "*\"").Replace("\":", "\"#").ToString();
//取出表名
var rg = new Regex(@"(?<={)[^:]+(?=:\[)", RegexOptions.IgnoreCase);
string strName = rg.Match(strJson).Value;
DataTable tb = null;
//去除表名
strJson = strJson.Substring(strJson.IndexOf("[") + );
strJson = strJson.Substring(, strJson.IndexOf("]"));
//获取数据
rg = new Regex(@"(?<={)[^}]+(?=})");
MatchCollection mc = rg.Matches(strJson);
for (int i = ; i < mc.Count; i++)
{
string strRow = mc[i].Value;
string[] strRows = strRow.Split('*');
//创建表
if (tb == null)
{
tb = new DataTable();
tb.TableName = strName;
foreach (string str in strRows)
{
var dc = new DataColumn();
string[] strCell = str.Split('#');
if (strCell[].Substring(, ) == "\"")
{
int a = strCell[].Length;
dc.ColumnName = strCell[].Substring(, a - );
}
else
{
dc.ColumnName = strCell[];
}
tb.Columns.Add(dc);
}
tb.AcceptChanges();
}
//增加内容
DataRow dr = tb.NewRow();
for (int r = ; r < strRows.Length; r++)
{
dr[r] = strRows[r].Split('#')[].Trim().Replace(",", ",").Replace(":", ":").Replace("\"", "");
}
tb.Rows.Add(dr);
tb.AcceptChanges();
}
return tb;
}
#endregion

现在连接也有了,最后一般是下载了,我们来分析单个链接。

没有了cookie,我们只要一个一个链接请求下载就可以了。于是我有了两个方法,肯定不止这两个了。

方法一,模仿上面那个有cookie的

 public void  GetContent2(string url, string destFileName, string method)
{
HttpWebResponse response = null;
HttpWebRequest request = null;
try
{ // 设置参数
//request.Host = "183.60.137.147:80";
// request.Address[""] = new Uri(""); request = WebRequest.Create(url) as HttpWebRequest;
request.AllowAutoRedirect = true;
request.Host = "forumimg01.touchcdn.com";
request.Method = method.ToUpper();
request.Headers["Accept-Encoding"] = "gzip, deflate, sdch";
request.ContentType = "image/webp,image/*,*/*;q=0.8";
string userAgent = string.Format("Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36)");//这个根据电脑不同而改变
request.UserAgent = userAgent; //发送请求并获取相应回应数据
response = request.GetResponse() as HttpWebResponse;
//直到request.GetResponse()程序才开始向目标网页发送Post请求
using (Stream instream = response.GetResponseStream())
{ //返回结果网页(html)代码
using (FileStream fileStream = new FileStream(destFileName, FileMode.Create))
{
instream.CopyTo(fileStream);//stream 写入到fileStream中
} }
}
catch (Exception ex)
{
string err = ex.Message;
}
finally
{
if (response != null)
response.Close();
}
}

方法二,请求整个网页的

 private void DownloadImage2(string objUrl, string destFileName)
{
// string destFileName = Path.Combine(destDir, Path.GetFileName(objUrl));
HttpWebRequest request =
(HttpWebRequest)HttpWebRequest.Create(objUrl);
// 欺骗服务器判断URLReferer
// request.Referer = "http://image.baidu.com";
using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
{
if (response.StatusCode == HttpStatusCode.OK)
{
using (Stream stream = response.GetResponseStream())
{
using (FileStream fileStream = new FileStream(destFileName, FileMode.Create))
{
stream.CopyTo(fileStream);//stream 写入到fileStream中
}
}
}
else
{
throw new Exception("下载" + objUrl + "失败,错误码:" + response.StatusCode);
}
}
}

下载保存路径方法

   private void DownloadImage(string id, string objUrl)
{
destDir = txtSavePath.Text.Trim();
if (string.IsNullOrEmpty(destDir))
{
MessageBox.Show("请选择要保存的文件夹!");
return;
}
if (!destDir.EndsWith("\\"))
{
destDir += "\\";
} string destFileName = Path.Combine(destDir, Path.GetFileName(objUrl)); // string urlimg = objUrl + "?imageView/2/w/380/interface/1";这个下载下来是小图,所以不用它才是大图
DownloadImage2(objUrl, destFileName);
// GetContent2(objUrl, destFileName, "GET"); }

点击开始执行

   private void btnStart_Click(object sender, EventArgs e)
{
//1到20页
btnStart.Enabled = false;
for(int i= ;i<;i++){
getbasedata(i + );
}
}

调用请求方法

  public void getbasedata(int num)//num是页码
{
string url = string.Format("http://www.taqu.cn/ajax/Forum/getFindListByTab?tab_id=119&limit=10&score=1468547984&page={0}", num);
string method = "GET"; DataTable dt = JsonToDataTable(GetContent(method, url));
if (dt.Rows.Count > )
{
foreach (DataRow dr in dt.Rows)
{
//string urlimg = dr["img_url"].ToString()+ "?imageView/2/w/380/interface/1";
DownloadImage(dr["id"].ToString(), dr["img_url"].ToString().Replace(@"\", ""));//这里@符号阻止转义
//因为json转化为datatable连接变成这样的了——http:\/\/forumimg01.touchcdn.com\/index\/3e637fe0142008158574c24a278d7804.jpg
//正确的是——http://forumimg01.touchcdn.com/index/3e637fe0142008158574c24a278d7804.jpg 所以需要替换
}
}
}

忘了 这个全局的变量 private string destDir; // 目标文件夹

运行的效果

主要和关键代码都放上来了。

窗体代码去这里下载 博客最底端  http://www.cnblogs.com/edisonchou/p/4175190.html

能想到的,都写上了,其实还可以更完善,比如加上IP代理,,一些数据保存到数据库,不过这个工程太大了。这也不是我要写这篇文章的目的,我只要这些图片,这些乐趣。

原来认真做一件事,真的可以获得意想不到的的乐趣,也打开了自己兴趣之门。

原来我也可以做到,不要临渊羡鱼,自己动手一干!

参考:

http://www.cnblogs.com/edisonchou/p/4175190.html

http://www.cnblogs.com/haigege/p/5492177.html

一次爬虫实践学习(C#)的更多相关文章

  1. python爬虫入门10分钟爬取一个网站

    一.基础入门 1.1什么是爬虫 爬虫(spider,又网络爬虫),是指向网站/网络发起请求,获取资源后分析并提取有用数据的程序. 从技术层面来说就是 通过程序模拟浏览器请求站点的行为,把站点返回的HT ...

  2. Scrapy入门到放弃01:开启爬虫2.0时代

    前言 Scrapy is coming!! 在写了七篇爬虫基础文章之后,终于写到心心念念的Scrapy了.Scrapy开启了爬虫2.0的时代,让爬虫以一种崭新的形式呈现在开发者面前. 在18年实习的时 ...

  3. 设计爬虫Hawk背后的故事

    本文写于圣诞节北京下午慵懒的午后.本文偏技术向,不过应该大部分人能看懂. 五年之痒 2016年,能记入个人年终总结的事情没几件,其中一个便是开源了Hawk.我花不少时间优化和推广它,得到的评价还算比较 ...

  4. Scrapy框架爬虫初探——中关村在线手机参数数据爬取

    关于Scrapy如何安装部署的文章已经相当多了,但是网上实战的例子还不是很多,近来正好在学习该爬虫框架,就简单写了个Spider Demo来实践.作为硬件数码控,我选择了经常光顾的中关村在线的手机页面 ...

  5. Python 爬虫模拟登陆知乎

    在之前写过一篇使用python爬虫爬取电影天堂资源的博客,重点是如何解析页面和提高爬虫的效率.由于电影天堂上的资源获取权限是所有人都一样的,所以不需要进行登录验证操作,写完那篇文章后又花了些时间研究了 ...

  6. scrapy爬虫docker部署

    spider_docker 接我上篇博客,为爬虫引用创建container,包括的模块:scrapy, mongo, celery, rabbitmq,连接https://github.com/Liu ...

  7. scrapy 知乎用户信息爬虫

    zhihu_spider 此项目的功能是爬取知乎用户信息以及人际拓扑关系,爬虫框架使用scrapy,数据存储使用mongo,下载这些数据感觉也没什么用,就当为大家学习scrapy提供一个例子吧.代码地 ...

  8. 120项改进:开源超级爬虫Hawk 2.0 重磅发布!

    沙漠君在历时半年,修改无数bug,更新一票新功能后,在今天隆重推出最新改进的超级爬虫Hawk 2.0! 啥?你不知道Hawk干吗用的? 这是采集数据的挖掘机,网络猎杀的重狙!半年多以前,沙漠君写了一篇 ...

  9. Python爬虫小白入门(四)PhatomJS+Selenium第一篇

    一.前言 在上一篇博文中,我们的爬虫面临着一个问题,在爬取Unsplash网站的时候,由于网站是下拉刷新,并没有分页.所以不能够通过页码获取页面的url来分别发送网络请求.我也尝试了其他方式,比如下拉 ...

随机推荐

  1. IOS基础学习-2: UIButton

    IOS基础学习-2: UIButton   UIButton是一个标准的UIControl控件,UIKit提供了一组控件:UISwitch开关.UIButton按钮.UISegmentedContro ...

  2. mycat的读写分离设置

    官网:http://www.mycat.org.cn/ 一.jdk环境的安装1.安装jdk1.7,这是mycat推荐的jdk环境 2.之前是用yum安装的jdk1.6,首先查找下 #yum info ...

  3. js自定义弹出框

    js自定义弹出框: 代码如下 <html> <head><title>自定义弹出对话框</title> <style type ="te ...

  4. Spark 宏观架构&执行步骤

    Spark 使用主从架构,有一个中心协调器和许多分布式worker. 中心协调器被称为driver.Driver 和被称为executor 的大量分布式worker 通信 Driver 运行在它自己的 ...

  5. 一个node项目的框架搭建流程

    项目服务端编程语言node,前端js,数据库mongodb, 开发工具用webstorm. 使用express应用生成器,生成项目雏形. 安装应用生成器工具,命令是npm install expres ...

  6. ios上position:fixed失效问题

    手机端上的猫腻真是多啊~~~ 此起彼伏! 最近又遇到了 固定定位的底部导航在ios上被弹出去 此时内心1w+个草泥马奔过~~~~~~~~ 直接上解决方案: <div class="ma ...

  7. python垃圾回收机制的一些理解

    概览:       主要通过 引用计数来进行垃圾收集, 就是说,当一个对象没有被其他对象引用的时候,会释放掉内存.     但是会有一些循环引用的对象,通过上面的方法,是没有办法清除掉的.所以,pyt ...

  8. ACM集训的第一题

    对于一群NP(2<=NP<=10)个要互送礼物的朋友,郭铮鹏要确定每个人送出的钱比收到的多多少. 在这一个问题中,每个人都准备了一些钱来送礼物,而这些钱将会被平均分给那些将收到他的礼物的人 ...

  9. Python学习【第八篇】Set集合

    Set集合 set集合是无序,不能重复,可嵌套的序列 如何创建 li = [] dic = {"k1":123} se = {"123","456&q ...

  10. iOS,手势识别简单使用

    1.iOS目前支持的手势识别(6种) 2.点按手势和慢速拖动手势简单使用 iOS目前支持的手势识别(6种) UITapGestureRecognizer(点按) UIPinchGestureRecog ...