因为java课程设计需要设计一个新冠肺炎智能问答系统,需要从互联网上爬去有关的问答信息,经过尝试,发现百度知道与悟空问答的反爬机制较为全面,所以选择搜狗问答进行问答对爬取。
导入java爬虫包
使用java的HttpClient子项目进行爬虫,使用Jsoup进行网页解析。所以需要导入相应的java依赖。
maven:
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpcore</artifactId>
<version>4.4.13</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.8</version>
</dependency>
<dependency>
<groupId>org.jsoup</groupId>
<artifactId>jsoup</artifactId>
<version>1.12.1</version>
</dependency>
这里注意如果是web项目,需要httpcore和httpclient依赖都是最高版本,否则会报HttpRquest错误。
分析网页
1、使用搜狗搜索关键词“新冠肺炎",分析URL:
https://www.sogou.com/sogou?query=%E6%96%B0%E5%86%A0%E8%82%BA%E7%82%8E&insite=wenwen.sogou.com&pid=sogou-wsse-a9e18cb5dd9d3ab4&rcer=&page=2&ie=utf8
可以发现page=""表示页数,所以可以通过改变page后的参数来设置爬取的页数。
2、F12查看网页源代码
这些a标签中的href就是每个问答对的链接,通过Jsoup的
select("a[target='_blank']");
可以爬去到这些href具体代码见下文。
3、分析具体问答网页
获取到上述链接后,对每一个链接进行访问,进入到详细的问答页面,同样F12查看网页源代码,找到问题和答案部分。
同样对他们的标签进行分析,使用Jsoup进行解析,代码见下文。
4、字符处理
可以看到网页源代码中的答案有许多无效字符,使用正则表达式进行无效字符的剔除即可。
问题字数较少,所以仅保留中文字符即可,正则表达式:
replaceAll("[^\\u4E00-\\u9FA5]","")
针对答案的特殊字符,使用如下正则表达式:
replaceAll("[\\u25c6~\\u25c7]|[\\——]|[\\▲]|[\\△]]","")
replaceAll是java的一个函数。
完整代码
public class Spider {
//爬取搜狗问问页面的问答连接
private static ArrayList<String> hrefList=new ArrayList();
private static ArrayList<ArrayList<String>>questionAndAnswer=new ArrayList<>();
public static ArrayList<ArrayList<String>> getQuestionAndAnswer() {
return questionAndAnswer;
}
public static void getHref(int page){
CloseableHttpClient httpClient=HttpClients.createDefault();
CloseableHttpResponse response=null;
//悟空问答,搜索新冠肺炎url
HttpGet request=new HttpGet("https://www.sogou.com/sogou?query=%E6%96%B0%E5%86%A0%E8%82%BA%E7%82%8E&insite=wenwen.sogou.com&pid=sogou-wsse-a9e18cb5dd9d3ab4&rcer=&page="+page+"&ie=utf8");
//模拟浏览器请求头
request.setHeader("User-Agent","Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML,like Gecko) Chrome/74.0.3729.169 Safari/537.36");
try{
response=httpClient.execute(request);
if(response.getStatusLine().getStatusCode()==HttpStatus.SC_OK){
HttpEntity httpEntity = response.getEntity();
String html = EntityUtils.toString(httpEntity, "utf-8");
//System.out.println(html);
Document document=Jsoup.parse(html);
Elements postItems=document.getElementsByClass("fb");
for(Element postItem:postItems){
Elements link=postItem.select( );
//System.out.println(link.attr("href"));
hrefList.add(link.attr("href"));
}
}else {
System.out.println("返回状态不是200");
System.out.println(EntityUtils.toString(response.getEntity(), "utf-8"));
}
}catch (ClientProtocolException e){System.out.print("客户端协议异常:"+e);}
catch (IOException e){System.out.print("IO异常:"+e);}
finally {
HttpClientUtils.closeQuietly(response);
HttpClientUtils.closeQuietly(httpClient);
}
}
//返回一个数组,第一个元素为问题,第二个元素为答案
public static ArrayList<String> getQuestionAndAnswer(String url){
ArrayList<String>temp=new ArrayList<>();
CloseableHttpClient httpClient=HttpClients.createDefault();
CloseableHttpResponse response=null;
//搜狗问问,搜索新冠肺炎url
HttpGet request=new HttpGet(url);
request.setHeader("User-Agent","Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML,like Gecko) Chrome/74.0.3729.169 Safari/537.36");
try{
response=httpClient.execute(request);
if(response.getStatusLine().getStatusCode()==HttpStatus.SC_OK){
HttpEntity httpEntity = response.getEntity();
String html = EntityUtils.toString(httpEntity, "utf-8");
//System.out.println(html);
Document document=Jsoup.parse(html);
Element title=document.getElementById("question_title");
//System.out.print(title.text());
temp.add(title.text().replaceAll("[^\\u4E00-\\u9FA5]",""));
Elements preTag=document.getElementsByTag("pre");
//System.out.print(preTag.get(0).text());
temp.add(preTag.get(0).text().replaceAll("[\\u25c6~\\u25c7]|[\\——]|[\\▲]|[\\△]]",""));
}else {
System.out.println("返回状态不是200");
System.out.println(EntityUtils.toString(response.getEntity(), "utf-8"));
}
}catch (ClientProtocolException e){System.out.print("客户端协议异常:"+e);}
catch (IOException e){System.out.print("IO异常:"+e);}
finally {
HttpClientUtils.closeQuietly(response);
HttpClientUtils.closeQuietly(httpClient);
}
return temp;
}
//爬取前maxPage页的问题与答案
public static void spiderQuestionAndAnswer(int maxPage){
for(int i=1;i<=maxPage;i++){
getHref(i);
}
for(String href:hrefList){
questionAndAnswer.add(getQuestionAndAnswer(href));
}
}
public static void main(String[] args){
int maxPage=3;
spiderQuestionAndAnswer(maxPage);
for(ArrayList<String>temp:questionAndAnswer){
for(String qa:temp){
System.out.println(qa);
}
System.out.println();
}
//正则表达式字符处理测试
/*String temp="为阴性达到出院标准】,,,,▲◇◆◇◆◇◆◇◆◇◆◇◆◇◆◇——————————";
System.out.print(temp.replaceAll("[^\\u4E00-\\u9FA5]",""));*/
}
}
新冠肺炎国内外疫情数据爬取见我的另一篇博客:
https://blog.csdn.net/NiZjiTouA/article/details/107094808