Python网络爬虫
- 处理验证码
- 移动端数据爬取
爬虫简介
- 什么是爬虫:
爬虫就是通过编写程序模拟浏览器上网,然后让其去互联网上抓取数据的过程。
- 爬虫的分类:
""" - 通用爬虫: 通用爬虫是搜索引擎(Baidu、Google、Yahoo等)“抓取系统”的重要组成部分。 主要目的是将互联网上的网页下载到本地,形成一个互联网内容的镜像备份。 简单来讲就是尽可能的;把互联网上的所有的网页下载下来,放到本地服务器里形成备分,在对这些网页做相关处理(提取关键字、去掉广告),最后提供一个用户检索接口。 - 聚焦爬虫: 聚焦爬虫是根据指定的需求抓取网络上指定的数据。例如:获取豆瓣上电影的名称和影评,而不是获取整张页面中所有的数据值。 """
- robts.txt 协议(君子协议):
""" 如果自己的门户网站中的指定页面中的数据不想让爬虫程序爬取到的话,那么则可以通过编写一个robots.txt的协议文件来约束爬虫程序的数据爬取。 robots协议的编写格式可以观察淘宝网的robots(访问www.taobao.com/robots.txt即可)。 但是需要注意的是,该协议只是相当于口头的协议,并没有使用相关技术进行强制管制,所以该协议是防君子不防小人。 """
- 反爬机制:
...
- 反反爬机制:
- 请求头伪装:
- 添加请求头:Connection: close
- 使用代理IP:
requests模块
- python自带的爬虫模块叫做urllib
- requests模块是第三方模块;基于网络请求的模块,其主要作用是用来模拟浏览器发起请求。
- 安装: pip install requests
- 代码逻辑步骤:
- 构建url;
- 伪装请求头;
- 发起请求;
- 持久化存储响应数据
- requests的get请求:
requests.get(url, params=None, **kwargs)
- url:指定URL;
- params:get请求后面添加的参数信息,为字典;
- **kwargs:可以添加请求头信息:headers={...}
- request的post请求:
requests.post(url, data=None, json=None, **kwargs)
- url: 指定的URL
- data:请求体的数据,常用;
- json:接受json数据,一般不用;
- **kwargs:可以添加请求头信息:headers={...},等详情见官网
- requests的响应体对象:
import requests url = "https://www.baidu.com/s?wd=requests" res = requests.get(url=url) # 获取响应的二进制响应体 content = res.content # 获取编码后的响应体 html = res.text # 更改编码格式,需要在text之前设置 res.encoding = "utf-8" # 获取响应体中的json数据并且反序列化 data = res.json() # 获取原始响应内容(响应头+响应体) raw = res.raw base_data = raw.read() # 获取响应头 headers = res.headers # 获取cookie, 类似字典,可以根据字典的方式取值 cookie = res.cookies print(cookie.get("BIDUPSID"))
- session处理cookie问题:
- 创建一个session对象,利用该对象发送请求,自动保存处理cookie
- 示例: 自动登录人人网
import requests if __name__ == "__main__": # 登录请求的url(通过抓包工具获取) post_url = 'http://www.renren.com/ajaxLogin/login?1=1&uniqueTimestamp=201873958471' # 创建一个session对象,该对象会自动将请求中的cookie进行存储和携带 session = requests.session() # 伪装UA headers = { 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36', } formdata = { 'email': '17701256561', 'icode': '', 'origURL': 'http://www.renren.com/home', 'domain': 'renren.com', 'key_id': '1', 'captcha_type': 'web_login', 'password': '7b456e6c3eb6615b2e122a2942ef3845da1f91e3de075179079a3b84952508e4', 'rkey': '44fd96c219c593f3c9612360c80310a3', 'f': 'https%3A%2F%2Fwww.baidu.com%2Flink%3Furl%3Dm7m_NSUp5Ri_ZrK5eNIpn_dMs48UAcvT-N_kmysWgYW%26wd%3D%26eqid%3Dba95daf5000065ce000000035b120219', } # 使用session发送请求,目的是为了将session保存该次请求中的cookie session.post(url=post_url, data=formdata, headers=headers) get_url = 'http://www.renren.com/960481378/profile' # 再次使用session进行请求的发送,该次请求中已经携带了cookie response = session.get(url=get_url, headers=headers) # 设置响应内容的编码格式 response.encoding = 'utf-8' # 将响应内容写入文件 with open('./renren.html', 'w') as fp: fp.write(response.text)
- proxies参数设置请求代理IP:
- 使用代理的意义:
- 一些网站会有相应的反爬虫措施,例如很多网站会检测某一段时间某个IP的访问次数,
如果访问频率太快以至于看起来不像正常访客,它可能就会会禁止这个IP的访问。
所以我们需要设置一些代理IP,每隔一段时间换一个代理IP,就算IP被禁止,依然可以换个IP继续爬取。
- 代理的分类:
- 正向代理:代理客户端获取数据。正向代理是为了保护客户端防止被追究责任。
- 反向代理:代理服务器提供数据。反向代理是为了保护服务器或负责负载均衡。
- 免费代理的网站:
- 代码示例,添加代理:
import random import requests url = "" headers = {} # 注册高匿代理 proxy_list = [ {"http": "112.115.57.20:3128"}, {'http': '121.41.171.223:3128'} ] # 随机获取一个代理进行使用 proxy = random.choice(proxy_list) res = requests.get(url, headers=headers, proxies=proxy)
- 线程池的使用:
- 基于multiprocessing.dummy线程池的数据爬取;
- pool.map()
- 爬取梨视频 示例解析:
import random import requests from multiprocessing.dummy import Pool url = "" headers = {} # 注册高匿代理 proxy_list = [ {"http": "112.115.57.20:3128"}, {'http': '121.41.171.223:3128'} ] # 随机获取一个代理进行使用 proxy = random.choice(proxy_list) res = requests.get(url, headers=headers, proxies=proxy) pool = Pool(10) url_list = [] res_list = pool.map(lambda x: requests.get(url=x, headers=headers), url_list) import requests import random import re from lxml import etree # 导入线程池模块 from multiprocessing.dummy import Pool # 实例化线程池对象 pool = Pool() url = 'http://www.pearvideo.com/category_1' headers = { 'User-Agent': "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.110 Safari/537.36" } # 获取首页页面数据 page_text = requests.get(url=url, headers=headers).text # 对获取的首页页面数据中的相关视频详情链接进行解析 tree = etree.HTML(page_text) li_list = tree.xpath('//div[@id="listvideoList"]/ul/li') detail_urls = [] # 存储二级页面的url for li in li_list: detail_url = 'http://www.pearvideo.com/' + li.xpath('./div/a/@href')[0] title = li.xpath('.//div[@class="vervideo-title"]/text()')[0] detail_urls.append(detail_url) vedio_urls = [] # 存储视频的url for url in detail_urls: page_text = requests.get(url=url, headers=headers).text vedio_url = re.findall('srcUrl="(.*?)"', page_text, re.S)[0] vedio_urls.append(vedio_url) # 使用线程池进行视频数据下载 func_request = lambda link: requests.get(url=link, headers=headers).content video_data_list = pool.map(func_request, vedio_urls) # 使用线程池进行视频数据保存 func_saveData = lambda data: save(data) pool.map(func_saveData, video_data_list) def save(data): fileName = str(random.randint(1, 10000)) + '.mp4' with open(fileName, 'wb') as fp: fp.write(data) print(fileName + '已存储') pool.close() pool.join()
数据解析三大方法
- re正则解析:
- 简单用法:
""" 单字符: . : 除换行以外所有字符 [] :[aoe] [a-w] 匹配集合中任意一个字符 \d :数字 [0-9] \D : 非数字 \w :数字、字母、下划线、中文 \W : 非\w \s :所有的空白字符包,括空格、制表符、换页符等等。等价于 [ \f\n\r\t\v]。 \S : 非空白 数量修饰: * : 任意多次 >=0 + : 至少1次 >=1 ? : 可有可无 0次或者1次 {m} :固定m次 hello{3,} {m,} :至少m次 {m,n} :m-n次 边界: $ : 以某某结尾 ^ : 以某某开头 分组: (ab) 贪婪模式: .* 非贪婪(惰性)模式: .*? re.I : 忽略大小写 re.M :多行匹配 re.S :单行匹配 # 爬虫必用 re.sub(正则表达式, 替换内容, 字符串) """
- lxml解析:
- 简介:
- 安装:
pip install lxml
- 用法:
- 解析HTML
import requests from lxml import etree url = "" headers = {} # 解析网络的HTML res = requests.get(url, headers=headers).text tree = etree.HTML(res) # 解析本地HTML文件 tree_local = etree.parse("./index.html")
- xpath解析:
- 解析语法:
import requests from lxml import etree url = "" headers = {} res = requests.get(url, headers=headers).text tree = etree.HTML(res) exp = "表达式" div = tree.xpath(exp)
- 常用表达式:
""" 属性定位: #找到class属性值为song的div标签 '//div[@class="song"] ' 层级&索引定位: #找到class属性值为tang的div的直系子标签ul下的第二个子标签li下的直系子标签a '//div[@class="tang"]/ul/li[2]/a' 逻辑运算: #找到href属性值为空且class属性值为du的a标签 '//a[@href="" and @class="du"]' 模糊匹配: '//div[contains(@class, "ng")]' '//div[starts-with(@class, "ta")]' 取文本: # /表示获取某个标签下的文本内容 # //表示获取某个标签下的文本内容和所有子标签下的文本内容 '//div[@class="song"]/p[1]/text()' '//div[@class="tang"]//text()' 取属性: '//div[@class="tang"]//li[2]/a/@href'
- bs4中Beautifulsoup:
- 安装:
- 更改pip 下载源
""" - 需要将pip源设置为国内源,阿里源、豆瓣源、网易源等 - windows (1)打开文件资源管理器(文件夹地址栏中) (2)地址栏上面输入 %appdata% (3)在这里面新建一个文件夹 pip (4)在pip文件夹里面新建一个文件叫做 pip.ini ,内容写如下即可 [global] timeout = 6000 index-url = https://mirrors.aliyun.com/pypi/simple/ trusted-host = mirrors.aliyun.com - linux (1)cd ~ (2)mkdir ~/.pip (3)vi ~/.pip/pip.conf (4)编辑内容,和windows一模一样 """
- 命令: pip install bs4; bs4 依赖 lxml 下载前请先安装 lxml
- 使用流程:
- 导包:from bs4 import BeautifulSoup - 使用方式:可以将一个html文档,转化为BeautifulSoup对象,然后通过对象的方法或者属性去查找指定的节点内容 (1)转化本地文件: - soup = BeautifulSoup(open('本地文件'), 'lxml') (2)转化网络文件: - soup = BeautifulSoup('字符串类型或者字节类型', 'lxml') (3)打印soup对象显示内容为html文件中的内容
- 匹配方法:
(1)根据标签名查找 - soup.a 只能找到第一个符合要求的标签 (2)获取属性 - soup.a.attrs 获取a所有的属性和属性值,返回一个字典 - soup.a.attrs['href'] 获取href属性 - soup.a['href'] 也可简写为这种形式 (3)获取内容 - soup.a.string - soup.a.text - soup.a.get_text() 【注意】如果标签还有标签,那么string获取到的结果为None,而其它两个,可以获取文本内容 (4)find:找到第一个符合要求的标签 - soup.find('a') 找到第一个符合要求的 - soup.find('a', title="xxx") - soup.find('a', alt="xxx") - soup.find('a', class_="xxx") - soup.find('a', id="xxx") (5)find_all:找到所有符合要求的标签 - soup.find_all('a') - soup.find_all(['a','b']) 找到所有的a和b标签 - soup.find_all('a', limit=2) 限制前两个 (6)根据选择器选择指定的内容 select:soup.select('#feng') - 常见的选择器:标签选择器(a)、类选择器(.)、id选择器(#)、层级选择器 - 层级选择器: div .dudu #lala .meme .xixi 下面好多级 div > p > a > .lala 只能是下面一级 【注意】select选择器返回永远是列表,需要通过下标提取指定的对象
selenium
- 环境搭建:
安装selenum:pip install selenium 获取某一款浏览器的驱动程序(以谷歌浏览器为例) 谷歌浏览器驱动下载地址:http://chromedriver.storage.googleapis.com/index.html 下载的驱动程序必须和浏览器的版本统一,大家可以根据http://blog.csdn.net/huilan_same/article/details/51896672 中提供的版本映射表进行对应
- 使用方法示例:
from selenium import webdriver #创建浏览器对象,通过该对象可以操作浏览器 browser = webdriver.Chrome('驱动路径')
# 使用其他驱动:# path = r'PhantomJS驱动路径'
# browser = webdriver.PhantomJS(path)
#使用浏览器发起指定请求 browser.get(url)
# 完毕后退出
browser.quit()
- 用法 代码详解:
from selenium import webdriver from time import sleep # 后面是你的浏览器驱动位置,记得前面加r'','r'是防止字符转义的 driver = webdriver.Chrome(r'E:\MyProject\spider\utils\chromedriver.exe') # 用get打开百度页面 driver.get("http://www.baidu.com") # 查找页面的“设置”选项,并进行点击 driver.find_elements_by_link_text('设置')[0].click() sleep(2) # 打开设置后找到“搜索设置”选项,设置为每页显示50条 driver.find_elements_by_link_text('搜索设置')[0].click() sleep(2) # 选中每页显示50条 m = driver.find_element_by_id('nr') sleep(2) m.find_element_by_xpath('//*[@id="nr"]/option[3]').click() m.find_element_by_xpath('.//option[3]').click() sleep(2) # 点击保存设置 driver.find_elements_by_class_name("prefpanelgo")[0].click() sleep(2) # 处理弹出的警告页面 确定accept() 和 取消dismiss() driver.switch_to_alert().accept() sleep(2) # 找到百度的输入框,并输入 美女 driver.find_element_by_id('kw').send_keys('美女') sleep(2) # 点击搜索按钮 driver.find_element_by_id('su').click() sleep(2) # 在打开的页面中找到“Selenium - 开源中国社区”,并打开这个页面 driver.find_elements_by_link_text('美女_百度图片')[0].click() sleep(3) # 关闭浏览器 driver.quit()
- 元素匹配的方法:
driver.find_element_by_id 根据id找节点
driver.find_elements_by_name 根据name找
driver.find_elements_by_xpath 根据xpath查找
driver.find_elements_by_tag_name 根据标签名找
driver.find_elements_by_class_name 根据class名字查找
- 谷歌无头浏览器:
1 import time 2 3 from selenium import webdriver 4 from selenium.webdriver.chrome.options import Options 5 6 # 创建一个参数对象,用来控制chrome以无界面模式打开 7 chrome_options = Options() 8 chrome_options.add_argument('--headless') 9 chrome_options.add_argument('--disable-gpu') 10 11 # 驱动路径 12 path = r"E:\MyProject\spider\utils\chromedriver.exe" 13 # 创建浏览器对象 14 browser = webdriver.Chrome(executable_path=path, chrome_options=chrome_options) 15 16 url = 'http://www.baidu.com/' 17 # 发送请求 18 browser.get(url) 19 time.sleep(3) 20 21 # 截图 22 browser.save_screenshot('baidu.png') 23 24 # 退出 25 browser.quit()
scrapy框架