对于具有大量数据的爬虫任务,单进程/线程就会显得捉襟见肘,爬取速度会比较慢,如果需要加快速度,就需要选择多线程/协程 进行处理;如果反爬虫中有对js代码进行加密的时候,一般的爬虫手段都会失效,那么解决的办法有一种就是,直接调用Selenium测试框架控制浏览器进行代码自动发送请求,对返回的真实页面的数据进行解析;在爬虫过程中,如果有验证码图片的时候,对于一般的黑白清晰字码,可以使用tesseract模块进行识别。
示例对比:爬取豆瓣电影top250
# -*- coding:utf-8 -*-
import requests
import json, time
from lxml import etree
class Spider_Douban(object):
def __init__(self):
self.base_url = 'https://movie.douban.com/top250?filter=&start='
self.headers = {
'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.181 Safari/537.36'}
self.data_list = []
# 发送请求
def send_request(self, url):
data = requests.get(url, headers=self.headers).content
return data
# 数据解析
def parse_data(self, data):
html = etree.HTML(data)
data_list = html.xpath('//*[@id="content"]/div/div[1]/ol/li/div/div[2]/div[1]/a/span[1]/text()')
for data in data_list:
self.data_list.append(data)
# 保存数据到本地
def save_data(self):
data_json = json.dumps(self.data_list)
with open('03_spider_douban.json', 'w') as f:
f.write(data_json)
# 主逻辑
def main(self):
import time
start_time = time.time()
for page in range(0, 225 + 1, 25):
url = self.base_url + str(page)
# 发送数据
data = self.send_request(url)
# 数据解析
self.parse_data(data)
print '正在爬第--%d--页' % ((page / 25) + 1)
# 保存数据
self.save_data()
end_time = time.time()
time = end_time - start_time
print '单线程所需时间为:%s' % time
# 单线程所需时间为:2.55781912804
if __name__ == '__main__':
spider_douban = Spider_Douban()
spider_douban.main()
# -*- coding:utf-8 -*-
import threading
import time
class Test_Threads(object):
def change_value(self):
global a
a = 200
time.sleep(5)
print '这是修改a的子线程1,a = %d'%a
def read_value(self):
print '这是读取a的子线程2,a = %d'%a
def run(self,):
t1 = threading.Thread(target=self.change_value)
t1.start()
t1.join()
threading.Thread(target=self.read_value).start()
print '这是主线程'
if __name__ == '__main__':
a = 100
test_thread= Test_Threads()
test_thread.run()
# -*- coding:utf-8 -*-
import requests
import json
from multiprocessing.dummy import Pool
from lxml import etree
class Spider_Douban(object):
def __init__(self):
self.base_url = 'https://movie.douban.com/top250?filter=&start='
self.headers = {
'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.181 Safari/537.36'
}
self.data_list = []
# 发送请求,解析数据
def send_request(self, url):
data = requests.get(url, headers=self.headers).content
self.parse_data(data)
def parse_data(self, data):
html = etree.HTML(data)
data_list = html.xpath('//*[@id="content"]/div/div[1]/ol/li/div/div[2]/div[1]/a/span[1]/text()')
for data in data_list:
self.data_list.append(data)
# 保存数据到本地
def save_data(self):
data_json = json.dumps(self.data_list)
with open('03_spider_douban.json', 'w') as f:
f.write(data_json)
# 主逻辑
def main(self):
import time
start_time = time.time()
url_list = []
for page in range(0, 225 + 1, 25):
url = self.base_url + str(page)
url_list.append(url)
# 使用线程池处理
# 1.创建线程池
thread_pool = Pool(len(url_list))
# 2.给线程池添加任务
thread_pool.map(self.send_request, url_list)
# 3.关闭线程池
thread_pool.close()
# 4.将所有线程join到主线程中
thread_pool.join()
# 保存数据
self.save_data()
end_time = time.time()
time = end_time - start_time
print '线程池所需时间为:%s' % time
# 线程池所需时间为:2.41724801064
if __name__ == '__main__':
spider_douban = Spider_Douban()
spider_douban.main()
# !/usr/bin/env python
# _*_ coding:utf-8 _*_
import requests
from lxml import etree
import time
import gevent
from gevent import monkey
monkey.patch_all()
class Douban_Spider(object):
def __init__(self):
self.base_url = 'https://movie.douban.com/top250?filter=&start='
self.headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; rv:11.0) like Gecko"}
self.count = 0
# 请求数据
def send_request(self, url):
time.sleep(1)
try:
data = requests.get(url, headers=self.headers).content
self.analysis_data(data)
except Exception, err:
print err
# 解析数据
def analysis_data(self, data):
# 1.转换类型
html_data = etree.HTML(data)
# 2.解析 -->list
data_list = html_data.xpath('//*[@id="content"]/div/div[1]/ol/li/div/div[2]/div[1]/a/span[1]/text()')
for name in data_list:
print name
self.count += 1
# 调度的方法
def start_work(self):
# 开始时间
start_time = time.time()
gevent_list = []
for page in range(0, 225 + 1, 25):
url = self.base_url + str(page)
print url
# 1.创建协程
gevent = gevent.spawn(self.send_request, url)
gevent_list.append(gevent)
# 2. 让主线程等待, 协程执行完毕
gevent.joinall(gevent_list)
end_time = time.time()
print end_time - start_time
print self.count
if __name__ == '__main__':
tool = Douban_Spider()
tool.start_work()
对于那些使用了ajax请求和DHTML技术的网页,传统的直接请求url方式就行不通了,因为那样只能请求到没有被执行js代码前的页面,即不完整的页面,解决这个问题的办法,有两个:
Selenium就是这样一个库,它是一个自动化测试工具,也可以用于爬虫开发,它可以自动化控制浏览器(常规浏览器,谷歌,火狐,甚至无界面浏览器,PhantomJS),利用selenium控制浏览器请求网页,返回响应,然后抓取响应的网页内容进行解析,就能解决,使用了ajax,DHTML技术,不能直接请求url提取信息的问题。
Phantoms是一种无界面的浏览器,因为不需要加载界面信息,所以请求加载页面,返回结果速度比有界面的浏览器要快速的多。
完整使用步骤代码:
# -*- coding:utf-8 -*-
from selenium import webdriver
import time
def base_use_selenium():
url = 'https://www.so.com/'
# 1.创建浏览器对象
driver = webdriver.PhantomJS()
# 2.请求数据
driver.get(url)
# 4.获取数据
data = driver.page_source # 格式为unicode,如需保存需要:data.encode('utf-8')
# 5.点击新闻按钮
# 获取按钮对象
button = driver.find_element_by_xpath('//*[@id="bd_tabnav"]/nav/a[2]')
# 点击按钮
button.click()
# 6.在输入框中输入内容
# 获取输入框对象
element = driver.find_element_by_id('haosou-input')
# 输入内容
element.send_keys(u'中兴')
# 7.点击搜索按钮(放大镜)进行
driver.find_element_by_xpath('//*[@id="search-form"]//div/button').click()
# 8.获取当前的页面
print driver.window_handles
# 9.切换页面
driver.switch_to_window(driver.window_handles[1])
# 10.获取cookies
cookies = driver.get_cookies()
# 11.获取当前页面的url
current_url = driver.current_url
# 3.保存快照
driver.save_screenshot('so.png')
# 12.关闭浏览器
driver.quit()
if __name__ == '__main__':
base_use_selenium()
代码:
-*- coding:utf-8 -*-
from selenium import webdriver
import time
def login_douban():
# 1.登录的网址
url = 'http://www.douban.com/accounts/login?source=movie'
# 2.创建浏览器对象
driver = webdriver.PhantomJS()
# 3.请求url
driver.get(url)
# 4.输入用户名
driver.find_element_by_id('email').send_keys(u'[email protected]')
# 5.输入密码
driver.find_element_by_id('password').send_keys(u'ALARMCHIME')
# 6.点击登录按钮,看是否出现验证码
driver.find_element_by_name('login').click()
time.sleep(2) # 等待页面加载完成
# 保存快照,查看验证码,手动输入,此处若使用第三方SDK(某云,极验)可以将验证码图片当参数传给第三方接口
# 第三方平台处理好后,返回结果,直接输入结果即可,这里不调用,所以要手动输入
driver.save_screenshot('veri_code.png')
code = raw_input('请输入验证码')
# 7.输入验证码
driver.find_element_by_id('captcha_field').send_keys(code)
# 8.点击登录按钮
driver.find_element_by_name('login').click()
driver.save_screenshot('douban.png')
if __name__ == '__main__':
login_douban()
# -*- coding:utf-8 -*-
from selenium import webdriver
import time
def login_douban():
# 1.登录的网址
url = 'http://www.douban.com/accounts/login?source=movie'
# 2.创建浏览器对象
driver = webdriver.PhantomJS()
# 3.请求url
driver.get(url)
# 4.输入用户名
driver.find_element_by_id('email').send_keys(u'[email protected]')
# 5.输入密码
driver.find_element_by_id('password').send_keys(u'ALARMCHIME')
# 6.点击登录按钮,看是否出现验证码
driver.find_element_by_name('login').click()
time.sleep(2) # 等待页面加载完成
# 保存快照,查看验证码,手动输入,此处若使用第三方SDK(某云,极验)可以将验证码图片当参数传给第三方接口
# 第三方平台处理好后,返回结果,直接输入结果即可,这里不调用,所以要手动输入
driver.save_screenshot('veri_code.png')
code = raw_input('请输入验证码')
# 7.输入验证码
driver.find_element_by_id('captcha_field').send_keys(code)
# 8.点击登录按钮
driver.find_element_by_name('login').click()
driver.save_screenshot('douban.png')
if __name__ == '__main__':
login_douban()