PythonCrawler urllib 多线程获取proxy池爬取数据

本文介绍了一个使用Python实现的多线程爬虫,通过获取代理IP池来提升爬取速度。在爬取过程中,为了避免IP被封,通过time.sleep和随机策略控制爬取速度,并使用线程锁确保文件写入的同步。同时,代码中展示了如何处理HTTPS请求和设置代理。然而,由于网络波动和反爬策略,有时仍可能出现网络错误。

此次实验使用多线程获取proxy池来增加爬虫爬取下载数据的速度,顺便测试一下urllib handler代理

注意由于线程爬取数据过快可能会导致ip被封,要适当增加time.sleep否则会出现503错误,也可以改变获取proxy_ip的策略,我这里是采取random随机获取proxy列表下标,也可以试试轮询策略,但注意要加锁

from urllib import request as ure
import random
import threading
import time
import ssl

proxies_pool = []

# 全局取消证书验证,避免访问https网页报错
ssl._create_default_https_context = ssl._create_unverified_context

file_count = 0  # 文件现个数

def init_request():
    # url = 'http://www.baidu.com/s?ie=UTF-8&wd=ip'
    url = 'https://dev.kdlapi.com/testproxy'

    headers = {
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/101.0.4951.41 Safari/537.36 Edg/101.0.1210.32'
    }

    request = ure.Request(url=url, headers=headers)

    return request


def init_proxy_ip(id):
    global proxies_pool

    # 提取代理API接口,获取1个代理IP
    api_url = "http://dps.kdlapi.com/api/getdps/?orderid=xxxxxxx6&num=1&signature=xxxxx&pt=1&sep=1"

    time.sleep(1)
    proxy_ip = ure.urlopen(api_url).read().decode('utf-8')

    username = '账号'
    password = '密码'

    proxies = {
        "http": "http://%(user)s:%(pwd)s@%(proxy)s/" % {"user": username, "pwd": password, "proxy": proxy_ip},
        "https": "http://%(user)s:%(pwd)s@%(proxy)s/" % {"user": username, "pwd": password, "proxy": proxy_ip}
    }

    proxies_pool.append(proxies)
    print('get proxy_ip_%d successfully' % id)


def init_proxy_ips(count=10):
    for ip in range(count):
        init_proxy_ip(ip)


def get_proxy_ip():
    proxies = random.choice(proxies_pool)
    return proxies


def get_data(request, proxies):
    handler = ure.ProxyHandler(proxies=proxies)

    opener = ure.build_opener(handler)

    response = opener.open(request)

    content = response.read().decode('utf-8')
    return content


def create_file(content):
    filename = 'proxy_test_%d.html' % file_count
    with open(filename, 'w', encoding='utf-8') as fp:
        fp.write(content)


class ProxyThread(threading.Thread):
    def __init__(self, thead_id, thread_name, delay):
        threading.Thread.__init__(self)
        self.thread_id = thead_id
        self.name = thread_name
        self.delay = delay

    def run(self):
        print('start: %s' % self.name)
        # print_time(self.name, self.delay, 3)
        fetch_data(self.name, self.delay, count=10)



def fetch_data(name, delay, count=5):
    global file_count
    while count:
        print('%s: get_data_%d -----%s' % (name, count, time.ctime(time.time())))
        proxies = get_proxy_ip()
        res = get_data(request, proxies)
        
        thread_lock.acquire()  # 上锁
        
        file_count += 1
        print('lock: %s' % name)
        create_file(content=res)
        print('unlock: %s' % name)
        
        thread_lock.release()  # 解锁
        
        time.sleep(delay)  # 阻塞 0.5s
        count -= 1

# def print_time(name, delay, counter):
#     while counter:
#         time.sleep(delay)
#         print('%s: %s' % (name, time.ctime(time.time())))
#         counter -= 1


request = init_request()  # 获取 request
init_proxy_ips()  # 初始化 proxy_ips 默认值为10个ip

threads = []
thread_lock = threading.Lock()  # 初始化线程锁

thread1 = ProxyThread(1, "ProxyThread-1", 1)
thread2 = ProxyThread(2, "ProxyThread-2", 1)

thread1.start()
thread2.start()

threads.append(thread1)
threads.append(thread2)

# 主线程必须等所有线程执行完才能执行之后的语句
for thread in threads:
    thread.join()  # 主线程执行该join方法

print('main stop')

 


现在来测试一下爬取百度搜索引擎搜索ip页面的数据,发现https可以正常爬取

应该是由于百度的反爬,有时候能访问有时候又报网络错误,所以我在请求头加上了cookie等一堆参数

from urllib import request as ure, parse as upa
import ssl

# # 全局取消证书验证,避免访问https网页报错
ssl._create_default_https_context = ssl._create_unverified_context

# # 提取代理API接口,获取1个代理IP
api_url = "http://dps.kdlapi.com/api/getdps/?orderid=xxxxxx&num=1&signature=xxxx&pt=1&sep=1"

proxy_ip = ure.urlopen(api_url).read().decode('utf-8')

username = 'xxxx'
password = 'xxxx'

proxies = {
    "http": "http://%(user)s:%(pwd)s@%(proxy)s/" % {"user": username, "pwd": password, "proxy": proxy_ip},
    "https": "http://%(user)s:%(pwd)s@%(proxy)s/" % {"user": username, "pwd": password, "proxy": proxy_ip}
}

url = 'https://www.baidu.com/s?ie=UTF-8&wd=ip'

headers = {
    'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9',
    # 'Accept-Encoding': 'gzip, deflate, br',
    'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8',
    'Cache-Control': 'max-age=0',
    'Connection': 'keep-alive',
    'Cookie': 'xxxx',
    'Host': 'www.baidu.com',
    'sec-ch-ua': '" Not A;Brand";v="99", "Chromium";v="101", "Microsoft Edge";v="101"',
    'sec-ch-ua-mobile': '?0',
    'sec-ch-ua-platform': '"Windows"',
    'Sec-Fetch-Dest': 'document',
    'Sec-Fetch-Mode': 'navigate',
    'Sec-Fetch-Site': 'none',
    'Sec-Fetch-User': '?1',
    'Upgrade-Insecure-Requests': '1',
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/101.0.4951.41 Safari/537.36 Edg/101.0.1210.32',
}

request = ure.Request(url=url, headers=headers)

# proxies = {
#     'http': '223.242.66.204:23401'
# }

handler = ure.ProxyHandler(proxies=proxies)

opener = ure.build_opener(handler)

response = opener.open(request)

# response = ure.urlopen(request)

content = response.read().decode('utf-8')

with open('ip.html', 'w', encoding='utf-8') as fp:
    fp.write(content)

<think>好的,用户想用Python写一个功能强大的爬虫,特别是要有图片爬虫能力。首先,我需要考虑用户可能的需求。他们可能想要从网站上批量下载图片,可能是为了数据分析、收集素材或者其他用途。用户可能对爬虫的基本概念有所了解,但需要具体的实现步骤和代码示例。 接下来,我要确定使用哪些Python库。常见的爬虫库有requests和Scrapy。对于图片爬虫,requests和BeautifulSoup组合可能更简单,适合新手。而Scrapy适合更复杂的项目,但学习曲线较陡。用户提到“功能强大”,可能希望扩展性好的,所以可能需要介绍Scrapy,但也要给出一个简单的示例。 然后,图片下载部分需要考虑如何从网页中提取图片链接。使用BeautifulSoup解析HTML,找到所有的img标签,获取src属性。同时,可能需要处理相对路径,转换成绝对路径,这里可以用urljoin方法。此外,要处理可能的异常,比如网络请求失败、图片存储问题等,确保爬虫的健壮性。 存储方面,需要创建目录来保存图片,使用os库来检查目录是否存在,不存在则创建。下载图片时,使用流传输模式,避免大文件占用过多内存。还要设置User-Agent头,模拟浏览器访问,防止被网站封禁。 用户可能还关心爬虫的效率,比如多线程或异步处理。但作为基础示例,可能先不涉及这些,但可以提到后续优化方向,比如使用aiohttp或Scrapy的异步功能。另外,遵守robots.txt和网站条款也是需要注意的点,避免法律问题。 最后,需要提供一个完整的代码示例,展示从发送请求、解析HTML、提取链接到下载图片的整个过程。同时,提醒用户注意合法使用爬虫,尊重版权,避免对目标网站造成过大负担。可能还需要考虑反爬机制,比如设置延迟、使用代理等,但基础示例中可能暂时不涉及,不过可以提到这些扩展点。</think>以下是一个使用 Python 编写的功能强大的图片爬虫示例,包含异常处理、多格式支持、并发下载等特性,使用 `requests` + `BeautifulSoup` + `concurrent.futures` 实现: ```python import os import requests from bs4 import BeautifulSoup from urllib.parse import urljoin, urlparse from concurrent.futures import ThreadPoolExecutor class ImageCrawler: def __init__(self, base_url, download_dir='downloads', max_workers=5): self.base_url = base_url self.download_dir = download_dir self.max_workers = max_workers self.visited_urls = set() self.image_extensions = ('.jpg', '.jpeg', '.png', '.gif', '.webp', '.bmp') self.headers = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36' } # 创建下载目录 os.makedirs(self.download_dir, exist_ok=True) def is_valid_url(self, url): """验证 URL 是否合法""" parsed = urlparse(url) return bool(parsed.netloc) and bool(parsed.scheme) def get_absolute_url(self, url): """将相对 URL 转换为绝对 URL""" return urljoin(self.base_url, url) def is_image_url(self, url): """检查 URL 是否是图片链接""" return url.lower().endswith(self.image_extensions) def download_image(self, img_url): """下载单个图片""" try: response = requests.get(img_url, headers=self.headers, stream=True, timeout=10) if response.status_code == 200: # 从 URL 中提取文件名 filename = os.path.basename(urlparse(img_url).path) save_path = os.path.join(self.download_dir, filename) # 避免重复下载 if not os.path.exists(save_path): with open(save_path, 'wb') as f: for chunk in response.iter_content(1024): f.write(chunk) print(f"下载成功: {filename}") else: print(f"文件已存在: {filename}") except Exception as e: print(f"下载失败 {img_url}: {str(e)}") def extract_images(self, url): """提取页面中的所有图片链接""" try: response = requests.get(url, headers=self.headers, timeout=10) response.raise_for_status() soup = BeautifulSoup(response.text, 'html.parser') # 查找所有图片标签 img_tags = soup.find_all('img') img_urls = [] for img in img_tags: img_url = img.attrs.get('src') or img.attrs.get('data-src') if img_url: img_url = self.get_absolute_url(img_url) if self.is_image_url(img_url): img_urls.append(img_url) return img_urls except Exception as e: print(f"解析页面失败 {url}: {str(e)}") return [] def crawl(self, url=None): """开始爬取""" url = url or self.base_url if url in self.visited_urls: return self.visited_urls.add(url) print(f"正在爬取: {url}") img_urls = self.extract_images(url) # 使用线程并发下载 with ThreadPoolExecutor(max_workers=self.max_workers) as executor: executor.map(self.download_image, img_urls) if __name__ == "__main__": # 使用示例 crawler = ImageCrawler( base_url="https://example.com", # 替换为目标网站 download_dir="downloaded_images", max_workers=10 ) crawler.crawl() ``` 主要功能特点: 1. **并发下载**:使用线程加速下载过程 2. **智能 URL 处理**: - 自动转换相对路径为绝对路径 - 验证 URL 合法性 - 支持多种图片格式(jpg/png/gif/webp等) 3. **反爬策略**: - 自定义 User-Agent - 请求超时设置 - 流式下载大文件 4. **健壮性设计**: - 异常处理机制 - 防重复下载 - 自动创建下载目录 5. **扩展性**: - 可自定义最大并发数 - 方便添加代理支持 - 容易扩展新的图片格式 使用注意事项: 1. 遵守目标网站的 robots.txt 协议 2. 尊重版权和网站使用条款 3. 添加适当的请求延迟(可添加 `time.sleep()` 避免被封) 4. 对需要登录的网站需添加 cookie 处理 5. 建议添加代理支持(可修改 headers 添加 Proxy 配置) 增强建议: 1. 添加代理中间件 2. 实现自动翻页功能 3. 添加浏览器自动化(Selenium)应对动态加载 4. 实现分布式爬虫(Redis + Scrapy) 5. 添加图片去重功能(MD5校验) 6. 支持云存储(AWS S3/Aliyun OSS) 请根据实际需求调整参数和功能,使用时请遵守相关法律法规和网站服务条款。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值