Falando em rastreadores, usar URLConnection que vem com Java pode alcançar algumas funções básicas de rastreamento de páginas, mas para algumas funções mais avançadas, como processamento de redirecionamento e remoção de tags HTML, apenas usar URLConnection não é suficiente.
Aqui podemos usar o pacote jar de terceiros HttpClient.
A seguir, usamos HttpClient para simplesmente escrever uma demonstração que rastreia até o Baidu:
importar java.io.FileOutputStream;
importar java.io.InputStream;
importar java.io.OutputStream;
importar org.apache.commons.httpclient.HttpClient;
importar org.apache.commons.httpclient.HttpStatus;
importar org.apache.commons.httpclient.methods.GetMethod;
/**
*
* @autor CallMeWhy
*
*/
classe pública Aranha {
private static HttpClient httpClient = new HttpClient();
/**
* @param caminho
* Link para a página de destino
* @return Retorna um valor booleano, indicando se o download da página de destino é feito normalmente
* @throwsException
* Exceção IO ao ler fluxo de página da web ou gravar fluxo de arquivo local
*/
public static boolean downloadPage(String path) lança exceção {
//Definir fluxos de entrada e saída
EntradaStream entrada = nulo;
OutputStream saída = nulo;
// Obtém o método post
GetMethod getMethod = new GetMethod(caminho);
//Executa, retorna o código de status
int statusCode = httpClient.executeMethod(getMethod);
// Processa o código de status
// Para simplificar, apenas o código de status com valor de retorno 200 é processado
if (statusCode == HttpStatus.SC_OK) {
entrada = getMethod.getResponseBodyAsStream();
//Obtém o nome do arquivo na URL
String nome do arquivo = path.substring(path.lastIndexOf('/') + 1)
+ ".html";
// Obtém o fluxo de saída do arquivo
saída = novo FileOutputStream(nome do arquivo);
//Saída para arquivo
int tempByte = -1;
enquanto ((tempByte = input.read()) > 0) {
saída.write(tempByte);
}
//Fecha o fluxo de entrada
if (entrada! = nulo) {
input.close();
}
//Fecha o fluxo de saída
if (saída! = nulo) {
saída.close();
}
retornar verdadeiro;
}
retornar falso;
}
public static void main(String[] args) {
tentar {
// Pegue a página inicial e a saída do Baidu
Spider.downloadPage("http://www.baidu.com");
} catch (Exceção e) {
e.printStackTrace();
}
}
}
No entanto, esse rastreador básico não pode atender às necessidades de vários rastreadores.
Vamos primeiro apresentar o rastreador abrangente.
Acredito que todos estão familiarizados com o conceito amplo. Em termos simples, você pode entender os rastreadores amplos como este.
Pensamos na Internet como um gráfico direcionado supergrande. Cada link em uma página da web é uma borda direcionada, e cada arquivo ou página pura sem link é o ponto final do gráfico:
O rastreador em largura é um rastreador. Ele rastreia esse gráfico direcionado, começando no nó raiz e rastreando os dados de novos nós, camada por camada.
O algoritmo de passagem de largura é o seguinte:
(1) O vértice V é colocado na fila.
(2) Continue a execução quando a fila não estiver vazia, caso contrário o algoritmo estará vazio.
(3) Retire da fila, obtenha o nó principal V, visite o vértice V e marque que V foi visitado.
(4) Encontre o primeiro vértice adjacente col do vértice V.
(5) Se o vértice adjacente col de V não foi visitado, então col é colocado na fila.
(6) Continue procurando outros vértices adjacentes de V e vá para a etapa (5). Se todos os vértices adjacentes de V foram visitados, vá para a etapa (2).
De acordo com o algoritmo de passagem de largura, a ordem de passagem da imagem acima é: A-> B-> C-> D-> E-> F-> H-> G-> I, de modo que seja percorrida camada por camada .
O rastreador amplo, na verdade, rastreia uma série de nós iniciais, o que é basicamente o mesmo que a travessia do gráfico.
Podemos colocar os URLs das páginas que precisam ser rastreadas em uma tabela TODO e as páginas visitadas em uma tabela Visited:
O processo básico do rastreador abrangente é o seguinte:
(1) Compare o link analisado com o link da tabela Visitado. Se o link não existir na tabela Visitado, significa que não foi visitado.
(2) Coloque o link na tabela TODO.
(3) Após o processamento, obtenha um link da tabela TODO e coloque-o diretamente na tabela Visited.
(4) Continue o processo acima para a página web representada por este link. E assim por diante.
A seguir, criaremos um rastreador abrangente passo a passo.
Primeiro, projete uma estrutura de dados para armazenar a tabela TODO. Considerando a necessidade do primeiro a entrar, primeiro a sair, usamos uma fila e customizamos uma classe Quere:
importar java.util.LinkedList;
/**
* Classe de fila personalizada para salvar a tabela TODO
*/
fila de classe pública {
/**
* Defina uma fila e implemente-a usando LinkedList
*/
private LinkedList<Object> queue = new LinkedList<Object>();
/**
* Adicione t à fila
*/
public void enQueue(Objeto t) {
fila.addLast(t);
}
/**
* Remova o primeiro item da fila e devolva-o
*/
objeto público deQueue() {
retornar fila.removeFirst();
}
/**
* Retorna se a fila está vazia
*/
public boolean isQueueEmpty() {
retornar fila.isEmpty();
}
/**
* Determine e retorne se a fila contém t
*/
contians booleanos públicos (Objeto t) {
retornar fila.contains(t);
}
/**
* Determine e retorne se a fila está vazia
*/
público booleano vazio() {
retornar fila.isEmpty();
}
}
Também é necessária uma estrutura de dados para registar os URLs que foram visitados, nomeadamente a tabela Visitados.
Considerando o papel desta tabela, sempre que uma URL for acessada, ela é primeiro pesquisada nesta estrutura de dados. Caso a URL atual já exista, a tarefa de URL é descartada.
Essa estrutura de dados precisa ser não duplicada e pode ser pesquisada rapidamente, por isso o HashSet é escolhido para armazenamento.
Resumindo, criamos outra classe SpiderQueue para salvar a tabela Visited e a tabela TODO:
importar java.util.HashSet;
importar java.util.Set;
/**
* Classe personalizada para salvar tabelas visitadas e tabelas não visitadas
*/
classe pública SpiderQueue {
/**
* A coleção de URLs visitados, ou seja, a tabela Visitada
*/
private static Set<Object> visitouUrl = new HashSet<>();
/**
* Adicionar à fila de URLs visitados
*/
public static void addVisitedUrl(String url) {
visitouUrl.add(url);
}
/**
* Remova URLs visitados
*/
public static void removeVisitedUrl(String url) {
visitouUrl.remove(url);
}
/**
* Obtenha o número de URLs visitados
*/
public static int getVisitedUrlNum() {
return visitUrl.size();
}
/**
* A coleção de URLs a serem visitadas, ou seja, a tabela não visitada
*/
fila estática privada unVisitedUrl = new Queue();
/**
* Obtenha a fila não visitada
*/
Fila estática pública getUnVisitedUrl() {
retornar unVisitedUrl;
}
/**
* UnVisitedUrl não visitado foi retirado da fila
*/
objeto estático público unVisitedUrlDeQueue() {
retornar unVisitedUrl.deQueue();
}
/**
* Certifique-se de que cada URL seja visitado apenas uma vez ao adicionar o URL a unVisitedUrl
*/
public static void addUnvisitedUrl(String url) {
if (url != null && !url.trim().equals("") && !visitedUrl.contains(url)
&& !unVisitedUrl.contians(url))
unVisitedUrl.enQueue(url);
}
/**
* Determine se a fila de URLs não visitados está vazia
*/
public static boolean unVisitedUrlsEmpty() {
retornar unVisitedUrl.empty();
}
}
O texto acima é um encapsulamento de algumas classes personalizadas. A próxima etapa é definir uma classe de ferramenta para baixar páginas da web.
controlador de pacote;
importar java.io.*;
importar org.apache.commons.httpclient.*;
importar org.apache.commons.httpclient.methods.*;
importar org.apache.commons.httpclient.params.*;
classe pública DownTool {
/**
* Gere o nome do arquivo da página da web a ser salva com base no URL e no tipo de página da web e remova caracteres que não sejam de nome de arquivo no URL
*/
private String getFileNameByUrl(String url, String contentType) {
//Remova os sete caracteres "http://"
url = url.substring(7);
//Confirme se a página capturada é do tipo texto/html
if (contentType.indexOf("html") != -1) {
// Converte todos os símbolos especiais em URLs em sublinhados
url = url.replaceAll("[//?/:*|<>/"]", "_") + ".html";
} outro {
url = url.replaceAll("[//?/:*|<>/"]", "_") + "."
+ contentType.substring(contentType.lastIndexOf("/") + 1);
}
url de retorno;
}
/**
* Salve a matriz de bytes da página da web em um arquivo local, filePath é o endereço relativo do arquivo a ser salvo
*/
private void saveToLocal(byte[] dados, String filePath) {
tentar {
Saída DataOutputStream = new DataOutputStream(new FileOutputStream(
novo arquivo(filePath)));
for (int i = 0; i < data.length; i++)
out.write(dados[i]);
fora.flush();
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
// Baixa a página web apontada pela URL
public String downloadFile(String url) {
String caminho do arquivo = null;
// 1. Gere o objeto HttpClinet e defina os parâmetros
HttpClient httpClient = new HttpClient();
//Define o tempo limite da conexão HTTP em 5s
httpClient.getHttpConnectionManager().getParams()
.setConnectionTimeout(5000);
// 2. Gere o objeto GetMethod e defina os parâmetros
GetMethod getMethod = new GetMethod(url);
//Define o tempo limite da solicitação de obtenção para 5s
getMethod.getParams().setParameter(HttpMethodParams.SO_TIMEOUT, 5000);
//Definir processamento de nova tentativa de solicitação
getMethod.getParams().setParameter(HttpMethodParams.RETRY_HANDLER,
new DefaultHttpMethodRetryHandler());
// 3. Executar solicitação GET
tentar {
int statusCode = httpClient.executeMethod(getMethod);
// Determina o código de status de acesso
if (statusCode! = HttpStatus.SC_OK) {
System.err.println("Método falhou: "
+ getMethod.getStatusLine());
caminho do arquivo = null;
}
// 4. Processar o conteúdo da resposta HTTP
byte[] responseBody = getMethod.getResponseBody(); // Ler como array de bytes
//Gera o nome do arquivo ao salvar com base no URL da página web
filePath = "temp//"
+ getNomeArquivoByUrl(url,
getMethod.getResponseHeader("Tipo de conteúdo")
.getValor());
saveToLocal(responseBody, filePath);
} catch (HttpException e) {
//Ocorre uma exceção fatal. Pode ser que o protocolo esteja incorreto ou que haja algo errado com o conteúdo retornado.
System.out.println("Por favor, verifique se o seu endereço http está correto");
e.printStackTrace();
} catch (IOException e) {
//Ocorre uma exceção de rede
e.printStackTrace();
} finalmente {
// Libera a conexão
getMethod.releaseConnection();
}
retornar caminho do arquivo;
}
}
Aqui precisamos de uma classe HtmlParserTool para lidar com tags HTML:
controlador de pacote;
importar java.util.HashSet;
importar java.util.Set;
importar org.htmlparser.Node;
importar org.htmlparser.NodeFilter;
importar org.htmlparser.Parser;
importar org.htmlparser.filters.NodeClassFilter;
importar org.htmlparser.filters.OrFilter;
importar org.htmlparser.tags.LinkTag;
importar org.htmlparser.util.NodeList;
importar org.htmlparser.util.ParserException;
importar modelo.LinkFilter;
classe pública HtmlParserTool {
// Obtém links em um site, o filtro é usado para filtrar links
public static Set<String> extracLinks(String url, filtro LinkFilter) {
Set<String> links = new HashSet<String>();
tentar {
Analisador analisador = novo Analisador(url);
parser.setEncoding("gb2312");
// Filtre a tag <frame>, usada para extrair o atributo src na tag frame
NodeFilter frameFilter = new NodeFilter() {
privado estático final longo serialVersionUID = 1L;
@Substituir
public boolean aceitar(Nó nó) {
if (node.getText().startsWith("frame src=")) {
retornar verdadeiro;
} outro {
retornar falso;
}
}
};
// OrFilter para definir a tag <a> e a tag <frame> do filtro
OrFilter linkFilter = new OrFilter(new NodeClassFilter(
LinkTag.class), frameFilter);
// Obtém todas as tags filtradas
Lista NodeList = parser.extractAllNodesThatMatch(linkFilter);
for (int i = 0; i < list.size(); i++) {
Tag do nó = list.elementAt(i);
if (tag instanceof LinkTag)// tag <a>
{
LinkTag link = tag (LinkTag);
String linkUrl = link.getLink();//URL
if (filtro.accept(linkUrl))
links.add(linkUrl);
} else // tag <frame>
{
// Extrai o link do atributo src no frame, como <frame src="test.html"/>
String frame = tag.getText();
int start = frame.indexOf("src=");
quadro = quadro.substring(início);
int fim = frame.indexOf(" ");
se (fim == -1)
fim = frame.indexOf(">");
String frameUrl = frame.substring(5, end - 1);
if (filtro.accept(frameUrl))
links.add(frameUrl);
}
}
} catch (ParserException e) {
e.printStackTrace();
}
links de retorno;
}
}
Finalmente, vamos escrever uma classe rastreadora para chamar a classe e função de encapsulamento anteriores:
controlador de pacote;
importar java.util.Set;
importar modelo.LinkFilter;
importar modelo.SpiderQueue;
classe pública BfsSpider {
/**
* Inicialize a fila de URL usando seed
*/
private void initCrawlerWithSeeds(String[] sementes) {
for (int i = 0; i < sementes.comprimento; i++)
SpiderQueue.addUnvisitedUrl(sementes[i]);
}
//Defina um filtro para extrair links começando com http://www.xxxx.com
rastreamento de void público(String[] sementes) {
Filtro LinkFilter = new LinkFilter() {
public boolean aceitar(String url) {
if (url.startsWith("http://www.baidu.com"))
retornar verdadeiro;
outro
retornar falso;
}
};
//Inicializa a fila de URL
initCrawlerWithSeeds(sementes);
// Condição do loop: o link a ser rastreado não está vazio e o número de páginas da web rastreadas não é superior a 1.000
enquanto (!SpiderQueue.unVisitedUrlsEmpty()
&& SpiderQueue.getVisitedUrlNum() <= 1000) {
// URL do cabeçalho da fila retirado da fila
String visitUrl = (String) SpiderQueue.unVisitedUrlDeQueue();
if (visitUrl == nulo)
continuar;
DownTool downLoader = new DownTool();
//Baixa a página da web
downLoader.downloadFile(visitUrl);
//Coloque esta URL na URL visitada
SpiderQueue.addVisitedUrl(visitUrl);
//Extraia a URL da página de download
Set<String> links = HtmlParserTool.extracLinks(visitUrl, filtro);
// Novos URLs não visitados são enfileirados
for (String link: links) {
SpiderQueue.addUnvisitedUrl(ligação);
}
}
}
//entrada do método principal
public static void main(String[] args) {
Rastreador BfsSpider = novo BfsSpider();
crawler.crawling(new String[] { "http://www.baidu.com" });
}
}
Depois de executá-lo, você pode ver que o rastreador rastreou todas as páginas da página do Baidu:
O texto acima é todo o conteúdo de java usando o kit de ferramentas HttpClient e o rastreador de largura para rastrear o conteúdo. É um pouco mais complicado, então os amigos devem pensar sobre isso com cuidado.