要实现一个爬虫系统,那么代理是一个绕不开的话题。如果经费充裕,当然优先考虑收费代理。因为众所周知,免费代理的稳定性和可用性都无法得到有效的保障。所以笔者在这里分享一个免费代理IP的搜集和校验的Demo,并基于此可以有效的支撑分布式爬取网易云音乐的数据。
代理IP爬虫&校验源码:ProxyIpSpider
网易云音乐爬虫源码:CloudMusicSpider【附带数据API】
网易云音乐爬虫实现逻辑:博客
欢迎Start、Issue
代理IP爬取
爬取三一代理、西刺代理和无忧代理三个网站上的所有免费代理IP信息,并持久化到mysql中,数据表结构如下所示:
CREATE TABLE `proxy_ip_info` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`proxy_ip` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=5085 DEFAULT CHARSET=utf8mb4;
IP有效性校验
注:由于免费IP大概率无法使用,或者存活时间较短;且IP爬取的速率远高于IP校验,所以不建议在爬取信息的同时进行有效性校验。
校验思路
挂代理访问待爬取的目标网站URL即可
实现方式
通过并发 + 定时轮询,实现高效且持续的校验
# -*- coding: utf-8 -*-
"""
代理Ip有效性校验
相对于爬取, IP的有效性校验比比较耗时的;
又因为Scrapy本身不支持并发, 所以在爬取IP的同时校验有效性会导致最终效率过于低下
可通过后台单独运行此脚本, 并基于进程池来提升校验效率
此处默认的进程数为10;校验等待超时时长为10s;
@Author YH YR
@Time 2018/10/24 17:19
"""
import random
import time
import multiprocessing
import requests
from ProxyIpSpider.utils.mysqlUtil import MysqlUtil
from ProxyIpSpider.utils.user_agents import agents
from ProxyIpSpider.settings import *
mysql = MysqlUtil(host, user_name, pass_word, db_name, port)
class IPValidityCheck:
@staticmethod
def run(processes_num=10):
"""
轮询代理IP表, 通过线程池提升IP筛选效率
在一轮筛选过后,暂停5分钟后继续筛选
:param processes_num:
:return:
"""
query_sql = 'select id, proxy_ip from proxy_ip_info'
while True:
results = mysql.query(query_sql, num='all')
if len(results) == 0:
print('IP table is empty')
return
pool = multiprocessing.Pool(processes=processes_num)
for i in results:
auto_increase_id = i[0]
ip = i[1]
pool.apply_async(proxy_ip_check, args=(ip, auto_increase_id))
pool.close()
pool.join()
print('Cycle check per minutes')
time.sleep(60)
def proxy_ip_check(check_ip, auto_increase_id, check_timeout=10):
"""
代理IP有效性检验
判断依据: 访问带爬取的目标地址, 根据请求返回结果初步判断有效性
:param check_ip: 待检查IP
:param auto_increase_id: 该数据在MySQL中对应的自增长Id
:param check_timeout: 请求超时时长; 如果在该时间内请求无响应, 则认为此Ip无效
:return:
"""
delete_sql = 'delete from proxy_ip_info where id = {0}'.format(auto_increase_id)
proxies = {'http': check_ip}
agent = random.choice(agents)
header = {'Referer': 'https://music.163.com/', 'User-Agent': agent}
try:
proxy_response = requests.get(request_check_url, proxies=proxies, headers=header, timeout=check_timeout)
if proxy_response.status_code == 200:
print('Valid IP: {0}'.format(check_ip))
else:
print('Invalid IP: {0}'.format(check_ip))
mysql.modify(delete_sql.format(auto_increase_id))
except:
print('Invalid IP: {0}'.format(check_ip))
mysql.modify(delete_sql.format(auto_increase_id))
if __name__ == '__main__':
IPValidityCheck.run()