忘了什么时候加的,iPad上的人人视频追剧了《我的天才女友》,没事的时候看了下,感觉还不错,进一步了解到原著那不勒斯四部曲,感觉视频进度有些慢,就想找找书看看,一时没找到【PS:购买实体书四十多块钱,虽然目前买得起,但是只是看看故事而不是收藏,不值得买,希望以后有机会补票,而且更习惯使用sp4看pdf或者多看多设备同步阅读进度】,不过找到了在线观看的网站,正好这一段时间有使用C#,就想着使用C#自动将内容抓取下来。断断续续的大概五六个小时的时间,终于功能上实现了。
由于没怎么研究过爬虫相关知识,不知道是否符合爬虫设计与实践的一些原则,自己做所的不过是通过webrequest抓取页面内容,然后人工分析寻找特征点,找到自己想要的内容。针对这次的任务首先通过目录抓取所有的章节以及对应的页面链接,然后分别抓取每个页面的内容,将章节和内容保存起来。目录中章节链接的提取和每个页面的内容提取都是通过观察尝试实验得到的,不知道是自己哪里设计出了问题,或者就是爬虫本身的特点,感觉很难写出通用的爬虫,很难得到通用的特征点,即使都是在线阅读站点,前端代码不一样,提取的特征都不一样,当前我是直接获取页面内容进行分析,也许会有一些成熟的库,可以直接提取所要的内容。
不管怎么说,折腾了一场,记录下来,以便以后需要的时候可以查看,而且存储在网络上可以防止丢失。
获取页面内容code:
/*
* 2019年1月25日09:04:14
* 输入网址,输出网址内容
* NOTE:
* 针对项目进行的特定开发,非通用,编码采取目标网址编码,目前选择GB2312
*/
public string GetContent(string url)
{
string rStr = "";
System.Net.WebRequest req = null;
System.Net.WebResponse resp = null;
System.IO.StreamReader iosr = null;
try
{
req = System.Net.WebRequest.Create(url);
resp = req.GetResponse();
iosr = new System.IO.StreamReader(resp.GetResponseStream(), Encoding.GetEncoding("gb2312"));
rStr = iosr.ReadToEnd();
//Console.WriteLine(rStr);
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
return rStr;
}
从目录页获得每个章节的名称以及链接
其中获得目标内容的起止标志zhangjie2 /ul就是通过观察实验得到的,目前采用的这种方法比较原始,分析页面内容,不同页面可能都不一样,不知道目前有没有成熟的框架可以方便迅捷的实现目的。
//从初始连接中获得每一个章节名称以及对应的链接。
List<KeyValuePair<string,string>>ParserContentToGetChapters(string str)
{
bool StartFlag=false;
string StartStr = "zhangjie2";
string Endstr = @"/ul";
// 章节 链接
List<KeyValuePair<string,string>>characters = new List<KeyValuePair<string,string>>();
Lexer lexer = new Lexer(str);
INode node = lexer.NextNode();
while(node!=null)
{
string local = node.GetText();
if(local.Contains(StartStr))
{
StartFlag = true;
}
if(local.Contains(Endstr))
{
StartFlag = false;
}
if(StartFlag)
{
if(local.Contains("href"))
{
List<string> tmp = this.GetHerfAndTitle(local);
characters.Add(new KeyValuePair<string, string>(tmp[1],tmp[0]));
}
}
node = lexer.NextNode();
}
return characters;
}
List<string> GetHerfAndTitle(string input)
{
List<string> ret = new List<string>();
string[] strs = input.Split('"');
int stageflag = 0;
foreach(string str in strs)
{
if(str.Contains("a href="))
{
stageflag = 1;
continue;
}
else if(str.Contains("title="))
{
stageflag = 2;
continue;
}
if(stageflag==0)
{
continue;
}
else if(stageflag==1)
{
ret.Add(websit + str);
}
else if(stageflag==2)
{
ret.Add(str);
break;
}
else
{
break;
}
}
return ret;
}
获得每个章节内容:
KeyValuePair<string,string> GetSinglePage(string Name,string url)
{
KeyValuePair<string, string> ret = new KeyValuePair<string, string>();
string content = "";
string tmp = this.GetContent(url);
content = this.SinglePageContent(tmp);
ret = new KeyValuePair<string, string>(Name, content);
return ret;
}
//从单个页面中找出所有的内容
string SinglePageContent(string all)
{
string ret = "";
bool StartFlag = false;
string StartStr = "div id=\"onearcxsbd\" class=\"onearcxsbd\"";
string Endstr = @"分页";
// 章节 链接
List<KeyValuePair<string, string>> characters = new List<KeyValuePair<string, string>>();
Lexer lexer = new Lexer(all);
INode node = lexer.NextNode();
while (node != null)
{
string local = node.GetText();
if (local.Contains(StartStr))
{
Console.WriteLine("start");
StartFlag = true;
node = lexer.NextNode();
continue;
}
else if(local == Endstr)
{
Console.WriteLine("end");
StartFlag = false;
}
if (StartFlag)
{
if(local == "br /")
{
}
else
{
ret += local;
}
}
node = lexer.NextNode();
}
ret = this.DealString(ret);
Console.WriteLine(ret);
return ret;
}
//将一些html中的转义字符恢复
string DealString(string input)
{
string ret = input;
Dictionary<string, string> localdict = new Dictionary<string, string>();
localdict.Add("·", "·");
localdict.Add("“", "“");
localdict.Add("”", "”");
localdict.Add("—", "—");
foreach (var tmp in localdict)
{
ret = ret.Replace(tmp.Key, tmp.Value);
}
return ret;
}
其他:
1.编码问题,后面需要能够自动获取页面所使用的编码,当初因为使用简繁体操作系统的缘故,浪费了不少时间;
2.最开始考虑使用Dictionary,不过Dictionary对顺序无法保证,改用List<KeyValuePair<string,string>>,防止章节顺序错乱;
3.统一汇总读写还是单条逐个读写,各有利弊,最终采取单个章节单独读写,同时有对内容进行汇总;
4.直接得到的html页面内容中,有些转义字符需要恢复本来的字符,比如· 转换为 · 等。
来源:oschina
链接:https://my.oschina.net/u/4308799/blog/3657239