1.一些回顾知识和补充
详解HttpURLConnection_清箫的博客-CSDN博客_httpurlconnection
一个是URL,一个是HeepURLConnection:
任何网络连接都需要经过socket才能连接,HttpURLConnection不需要设置socket,所以,HttpURLConnection并不是底层的连接,而是在底层连接上的一个请求。这就是为什么HttpURLConneciton只是一个抽象类,自身不能被实例化的原因。HttpURLConnection只能通过URL.openConnection()方法创建具体的实例。
这里请求头的话有Get 、Post,
GET请求和POST请求详述_bear*6的博客-CSDN博客_get请求和post的用法
GET 请求和 POST 请求的区别与使用示例 - 隔壁汪书 - 博客园
爬虫在setRequestProperty传入的:
User-Agent(用户代理)是什么_睿科知识云的博客-CSDN博客_user-agent
正则:
正则表达式中关于 \ 的用法,例如\. \(_懶好人vest的博客-CSDN博客_正则表达式\
正则 ?= 用法_风神修罗使的博客-CSDN博客_?= 正则
正则表达式中?=和?:和?!的理解_这个昵称没有被占用吧的博客-CSDN博客_正则表达式?!
接口和实现类
接口 对象 = new 实现类 与 实现类 对象= new 实现类_代码敲上天.的博客-CSDN博客_new 实现类
Set set=new HashSet();的意义是什么呢_谜的博客的博客-CSDN博客
Collection<? extends E>解释:
1.实现了Collection接口
2.类型一定是E的子类
?是“任意类”的意思,extends继承不多说,E是指定类型,是泛型;
通常出现在将一个集合赋值给另一个集合的情景中,
如:public LinkedList(Collection<? extends E> c )、addAll(Collection<? extends E> c);
addAll方法——向Set集合添加另一个集合的所有内容_xk_一步一步来的博客-CSDN博客_addall方法
2.究极详细的代码分析
public class UrlCrawBoke {
static String userId = "";
public static void main(String urlstr[]) throws IOException, InterruptedException {
//set是接口,hashset是它的实现类
//接口 对象 = new 实现类,见笔记,主要是为了实现多态,虽然这里不需要多态,但是这是良好的代码习惯
Set urls = new HashSet();
// ----------------------------------------------遍历每一页 获取文章链接----------------------------------------------
//该引用为常量,该值无法修改
final String homeUrl = "https://blog.csdn.net/" + userId + "/article/list/";// 后面加pageNum即可
//统计页数
int totalPage = 0;
//接受从网页爬取的内容
InputStream is;
//把网页内容转为string
String pageStr;
//网站地址索引
StringBuilder curUrl = null;
for (int i = 1; i < 50; i++) {
//暂停1秒
Thread.sleep(1000);
//找页数,如果写的文章多,那就有两页三页......
System.out.println("finding page " + i);
//初始化网站地址索引
curUrl = new StringBuilder(homeUrl);
//完善地址,加上页码
curUrl.append(i);
System.out.println(curUrl);
//爬取网站内容
is = doGet(curUrl.toString());
//转成string
pageStr = inputStreamToString(is, "UTF-8");// 一整页的html源码
//正则表达式,这里是来获取每一篇博客的网址,按照规律来,detaiLs后面会跟8-9个数字,
//又因为在博客目录下,要访问具体博客,我们鼠标点的是图标文字,但是实际上点的是超链接,也就是这个网页的静态资源里面html文本中必然是有href
//所以(?<=href=\")表示匹配以href="开头的字符串,并且捕获(存储)到分组中,\"写在正则里面就是表示",\是引用符
//而最后面的(?=\")表示匹配以"结尾的字符串,并且捕获(存储)到分组中,这样就把具体博客的网址拿下来了
List list = getMatherSubstrs(pageStr, "(?<=href=\")https://blog.csdn.net/" + userId + "/article/details/[0-9]{8,9}(?=\")");
//把网址加进去
/*这里的addall,有点东西的
首先首先,集合collection是最上层滴接口
然后set是collection的子接口
再然后Hashset是set其中一个实现类
然后按道理应该addall方法要在hashset中实现
但是呢,hashset不仅实现set还继承了abstractset这个抽象类
然后abstractset这个抽象类继承AbstractCollection(当然也实现了set)
所以最后最后呢,addall这个玩意是在AbstractCollection这个抽象类里面实现的,这也是为什么我们查找实现查到AbstractCollection去了
*/
//addall就是把list的内容全部添加到urls里去
urls.addAll(list);
//总页数
totalPage = i;
//如果pagestr里面没有空空如也这四个字,那就返回-1,至于空空如也是因为csdn里面如果没有博客那就会出现这里空空如也
//但是这网页有时候也脑抽的,不一定每次都吃
if (pageStr.lastIndexOf("空空如也") != -1) {
System.out.println("已经到最后一页!");
break;
} else {
System.out.println("Success~");
}
}
System.out.println("总页数为: " + totalPage);
// ---------------------------------------------------打印每个链接---------------------------------------------------
System.out.println("打印每个链接");
for (Object s:urls) {
System.out.println(s);
}
System.out.println("打印每个链接完毕");
// ---------------------------------------------------访问每个链接---------------------------------------------------
int i=0;
for (Object s:urls) {
//传入网址进行访问
doGet(s.toString());
System.out.println("成功访问第" + (++i) + "个链接,共" + urls.size() + "个:" + s);
}
// ---------------------------------------------------程序结束---------------------------------------------------
System.out.println("运行完毕,成功增加访问数:" + urls.size());
}
public static InputStream doGet(String urlstr) throws IOException {
//初始化URL
URL url = new URL(urlstr);
//打开URL链接,获得HttpURLConnection对象,下面的括号转化是有原理的,看看笔记
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
//对链接进行设置(设置请求头或响应头)
//setRequestProperty(key,value),后面只能跟一个value
//setRequestProperty会覆盖已经存在的key的所有values,有清零重新赋值的作用
//User-Agent(用户代理),详情看笔记
conn.setRequestProperty("User-Agent",
"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36");
//建立HttpURLConnection链接
conn.connect();
//读取网页内容(字符串流)(请求)(实际上也会隐式调用connect())
InputStream inputStream = conn.getInputStream();
//返回
return inputStream;
}
//charset代表的是编码
public static String inputStreamToString(InputStream is, String charset) throws IOException {
//缓冲区
byte[] bytes = new byte[1024];
//长度
int byteLength = 0;
StringBuffer sb = new StringBuffer();
//常见的读取操作
while ((byteLength = is.read(bytes)) != -1) {
//函数里面长度可以不写
//这个string()源码很有意思,虽然看起来吃力,但是值得一看
sb.append(new String(bytes, 0, byteLength, charset));
}
return sb.toString();
}
// 正则匹配
public static List getMatherSubstrs(String str, String regex) {
//获取链表
List list = new ArrayList();
//获取正则表达式对象
Pattern p = Pattern.compile(regex);
//获取文本匹配器m,拿着m,按p的规则去匹配str
Matcher m = p.matcher(str);
while (m.find()) {
//循环获取
list.add(m.group());
}
return list;
}
}