网页爬虫工具

免费在线网页爬虫工具,支持抓取网页文本、图片、链接等内容。自定义选择器、深度控制、并发请求,快速提取目标数据。适用于数据分析、内容采集等场景

目标网址

输入要抓取的网页URL

抓取设置

配置抓取参数

只抓取匹配选择器的内容

什么是网页爬虫?

网页爬虫(Web Spider)是一种自动抓取互联网信息的程序,通过模拟浏览器请求获取网页内容,然后解析提取所需数据。爬虫广泛应用于搜索引擎索引、数据分析、竞品监控、价格比较、内容聚合等领域。

网页爬虫的基本原理

  • 发送请求:向目标网站发送HTTP请求,获取HTML内容
  • 解析页面:使用HTML解析器解析返回的HTML文档
  • 提取数据:使用选择器或正则表达式提取所需数据
  • 存储数据:将提取的数据保存到数据库或文件中
  • 继续爬取:根据链接规则继续爬取其他页面

爬虫的分类

类型 特点 常用技术
静态爬虫 抓取静态HTML页面 Python + BeautifulSoup, Node.js + Cheerio
动态爬虫 执行JavaScript,抓取动态内容 Puppeteer, Selenium, Playwright
API爬虫 直接调用API接口获取数据 REST API, GraphQL
分布式爬虫 多节点协作爬取,大规模数据 Scrapy Cluster, Celery

常用爬虫框架

  • Python:Scrapy(功能强大)、BeautifulSoup(简单易用)、Requests + BeautifulSoup
  • Node.js:Puppeteer(支持动态页面)、Cheerio(类似jQuery)、Axios
  • Java:Jsoup(HTML解析)、HttpClient(HTTP请求)、WebMagic
  • Go:Colly(高性能爬虫框架)、net/http(标准库)

常见问题(FAQ)

Q: 网页爬虫合法吗?

A: 爬虫本身是合法的技术,但需要注意:1)遵守网站的robots.txt规定;2)不要对服务器造成过大压力;3)不要抓取有版权保护的内容;4)尊重用户隐私和敏感信息;5)遵守相关法律法规。

Q: 如何避免被封禁?

A: 避免被封禁的方法:1)设置合理的请求间隔(如1-3秒);2)使用代理IP池轮换;3)随机更换User-Agent;4)遵守robots.txt规则;5)控制并发请求数量;6)使用cookies维持会话。

Q: 如何处理动态网页?

A>动态网页需要使用浏览器自动化工具:1)Puppeteer(推荐,Node.js);2)Selenium(支持多语言);3)Playwright(微软开发);4)PhantomJS(已停止维护)。这些工具可以执行JavaScript并获取渲染后的页面内容。

Q: 什么是robots.txt?

A>robots.txt是网站根目录下的文本文件,告诉爬虫哪些页面可以抓取,哪些不可以。遵守robots.txt是爬虫的基本礼仪。可以使用爬虫框架内置的RobotsTxtParser来自动解析和遵守这些规则。

代码示例

JavaScript Fetch 抓取示例

// 使用 Fetch API 抓取网页内容
async function crawlWebPage(url) {
    try {
        const response = await fetch(url, {
            method: 'GET',
            headers: {
                'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)'
            }
        });
        
        if (!response.ok) {
            throw new Error(`HTTP error! status: ${response.status}`);
        }
        
        const html = await response.text();
        const parser = new DOMParser();
        const doc = parser.parseFromString(html, 'text/html');
        
        // 提取标题
        const title = doc.querySelector('title')?.textContent || '';
        
        // 提取所有链接
        const links = Array.from(doc.querySelectorAll('a[href]')).map(a => ({
            text: a.textContent.trim(),
            href: a.href
        }));
        
        // 提取所有图片
        const images = Array.from(doc.querySelectorAll('img[src]')).map(img => ({
            alt: img.alt || '',
            src: img.src
        }));
        
        // 提取Meta信息
        const meta = {};
        doc.querySelectorAll('meta').forEach(m => {
            if (m.name) meta[m.name] = m.content;
            if (m.property) meta[m.property] = m.content;
        });
        
        // 提取页面文本
        const text = doc.body?.textContent || '';
        
        return { title, links, images, meta, text };
    } catch (error) {
        console.error('抓取失败:', error);
        return null;
    }
}

// 使用示例
crawlWebPage('https://example.com').then(data => {
    if (data) {
        console.log('页面标题:', data.title);
        console.log('链接数量:', data.links.length);
        console.log('图片数量:', data.images.length);
        console.log('Meta信息:', data.meta);
    }
});

Python Requests + BeautifulSoup

import requests
from bs4 import BeautifulSoup
import json
import csv

def crawl_webpage(url, selector=None):
    """
    爬取网页内容
    :param url: 目标网址
    :param selector: CSS选择器(可选)
    :return: 包含网页数据的字典
    """
    headers = {
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)'
    }
    
    try:
        # 发送请求
        response = requests.get(url, headers=headers, timeout=10)
        response.raise_for_status()
        response.encoding = response.apparent_encoding
        
        # 解析HTML
        soup = BeautifulSoup(response.text, 'html.parser')
        
        # 如果指定了选择器,只提取该部分
        if selector:
            soup = soup.select_one(selector)
            if not soup:
                return {'error': '未找到匹配选择器的内容'}
        
        # 提取标题
        title = soup.find('title').get_text(strip=True) if soup.find('title') else ''
        
        # 提取所有链接
        links = []
        for a in soup.find_all('a', href=True):
            links.append({
                'text': a.get_text(strip=True),
                'href': a['href']
            })
        
        # 提取所有图片
        images = []
        for img in soup.find_all('img', src=True):
            images.append({
                'alt': img.get('alt', ''),
                'src': img['src']
            })
        
        # 提取Meta信息
        meta = {}
        for m in soup.find_all('meta'):
            name = m.get('name') or m.get('property')
            content = m.get('content')
            if name and content:
                meta[name] = content
        
        # 提取页面文本
        text = soup.get_text(strip=True, separator='\n')
        
        return {
            'title': title,
            'links': links,
            'images': images,
            'meta': meta,
            'text': text
        }
        
    except Exception as e:
        print(f'爬取失败: {e}')
        return {'error': str(e)}

def export_to_json(data, filename):
    """导出为JSON格式"""
    with open(filename, 'w', encoding='utf-8') as f:
        json.dump(data, f, ensure_ascii=False, indent=2)

def export_to_csv(data, filename):
    """导出为CSV格式"""
    if 'links' in data:
        with open(filename, 'w', encoding='utf-8', newline='') as f:
            writer = csv.writer(f)
            writer.writerow(['Text', 'URL'])
            for link in data['links']:
                writer.writerow([link['text'], link['href']])

# 使用示例
if __name__ == '__main__':
    url = 'https://example.com'
    data = crawl_webpage(url, selector='.content')
    
    if data and 'error' not in data:
        print(f'标题: {data["title"]}')
        print(f'链接数: {len(data["links"])}')
        print(f'图片数: {len(data["images"])}')
        
        # 导出数据
        export_to_json(data, 'output.json')
        export_to_csv(data, 'links.csv')
    else:
        print('爬取失败:', data.get('error'))

Node.js Puppeteer 爬虫

const puppeteer = require('puppeteer');
const fs = require('fs');
const path = require('path');

async function crawlWebPage(url, options = {}) {
    try {
        // 启动浏览器
        const browser = await puppeteer.launch({
            headless: options.headless !== false,
            args: ['--no-sandbox', '--disable-setuid-sandbox']
        });
        
        const page = await browser.newPage();
        
        // 设置User-Agent
        await page.setUserAgent(options.userAgent || 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)');
        
        // 设置视口
        await page.setViewport({ width: 1920, height: 1080 });
        
        // 访问页面
        await page.goto(url, { waitUntil: 'networkidle2', timeout: 30000 });
        
        // 等待选择器(如果指定)
        if (options.selector) {
            await page.waitForSelector(options.selector, { timeout: 5000 });
        }
        
        // 执行JavaScript提取数据
        const data = await page.evaluate((selector) => {
            // 如果指定了选择器,只提取该部分
            let root = document;
            if (selector) {
                const element = document.querySelector(selector);
                if (element) root = element;
            }
            
            // 提取标题
            const title = document.querySelector('title')?.textContent || '';
            
            // 提取所有链接
            const links = Array.from(root.querySelectorAll('a[href]')).map(a => ({
                text: a.textContent.trim(),
                href: a.href
            }));
            
            // 提取所有图片
            const images = Array.from(root.querySelectorAll('img[src]')).map(img => ({
                alt: img.alt || '',
                src: img.src
            }));
            
            // 提取Meta信息
            const meta = {};
            document.querySelectorAll('meta').forEach(m => {
                if (m.name) meta[m.name] = m.content;
                if (m.property) meta[m.property] = m.content;
            });
            
            // 提取页面文本
            const text = root.body?.textContent || '';
            
            return { title, links, images, meta, text };
        }, options.selector || '');
        
        // 截图
        if (options.screenshot) {
            await page.screenshot({ 
                path: options.screenshotPath || 'screenshot.png', 
                fullPage: true 
            });
        }
        
        await browser.close();
        
        return data;
    } catch (error) {
        console.error('爬取失败:', error);
        return null;
    }
}

async function crawlMultiplePages(urls, concurrency = 3) {
    // 并发爬取多个页面
    const results = [];
    const chunks = [];
    
    // 分块处理
    for (let i = 0; i < urls.length; i += concurrency) {
        chunks.push(urls.slice(i, i + concurrency));
    }
    
    for (const chunk of chunks) {
        const promises = chunk.map(url => crawlWebPage(url));
        const chunkResults = await Promise.all(promises);
        results.push(...chunkResults);
    }
    
    return results;
}

// 使用示例
async function main() {
    const url = 'https://example.com';
    const data = await crawlWebPage(url, {
        selector: '.content',
        screenshot: true,
        screenshotPath: 'screenshot.png'
    });
    
    if (data) {
        console.log('标题:', data.title);
        console.log('链接数:', data.links.length);
        console.log('图片数:', data.images.length);
        
        // 保存为JSON
        fs.writeFileSync('output.json', JSON.stringify(data, null, 2));
    }
}

main();

Java Jsoup 爬虫

import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class WebCrawler {
    
    public static Map crawlWebPage(String url, String selector) {
        Map result = new HashMap<>();
        
        try {
            // 连接并获取文档
            Document doc = Jsoup.connect(url)
                .userAgent("Mozilla/5.0 (Windows NT 10.0; Win64; x64)")
                .timeout(10000)
                .get();
            
            // 如果指定了选择器,只提取该部分
            if (selector != null && !selector.isEmpty()) {
                Element selected = doc.selectFirst(selector);
                if (selected != null) {
                    doc = Jsoup.parse(selected.outerHtml());
                }
            }
            
            // 提取标题
            String title = doc.title();
            result.put("title", title);
            
            // 提取所有链接
            List> links = new ArrayList<>();
            Elements linkElements = doc.select("a[href]");
            for (Element e : linkElements) {
                Map link = new HashMap<>();
                link.put("text", e.text().trim());
                link.put("href", e.absUrl("href"));
                links.add(link);
            }
            result.put("links", links);

            // 提取所有图片
            List> images = new ArrayList<>();
            Elements imgElements = doc.select("img[src]");
            for (Element e : imgElements) {
                Map img = new HashMap<>();
                img.put("alt", e.attr("alt"));
                img.put("src", e.absUrl("src"));
                images.add(img);
            }
            result.put("images", images);

            // 提取Meta信息
            Map meta = new HashMap<>();
            Elements metaElements = doc.select("meta[name], meta[property]");
            for (Element e : metaElements) {
                String name = e.attr("name");
                if (name.isEmpty()) name = e.attr("property");
                meta.put(name, e.attr("content"));
            }
            result.put("meta", meta);

            // 提取文本内容
            String text = doc.body().text();
            result.put("text", text);

            // 提取页面信息
            Map pageInfo = new HashMap<>();
            pageInfo.put("url", url);
            pageInfo.put("charset", doc.charset().name());
            result.put("pageInfo", pageInfo);

        } catch (Exception e) {
            System.err.println("爬取失败: " + e.getMessage());
            result.put("error", e.getMessage());
        }

        return result;
    }

    public static List> crawlMultiplePages(List urls, int concurrency) {
        List> results = new ArrayList<>();

        for (String url : urls) {
            try {
                results.add(crawlWebPage(url, null));
                Thread.sleep(1000); // 避免请求过快
            } catch (Exception e) {
                Map errorResult = new HashMap<>();
                errorResult.put("error", e.getMessage());
                results.add(errorResult);
            }
        }

        return results;
    }

    public static void main(String[] args) {
        String url = "https://example.com";
        Map data = crawlWebPage(url, ".content");

        System.out.println("标题: " + data.get("title"));

        @SuppressWarnings("unchecked")
        List> links = (List>) data.get("links");
        System.out.println("链接数: " + links.size());

        @SuppressWarnings("unchecked")
        List> images = (List>) data.get("images");
        System.out.println("图片数: " + images.size());

        // 输出JSON格式
        System.out.println("JSON输出:");
        System.out.println(data.toString());
    }
}