Python爬虫 笔记

概念相关

课程总览

基本概念

什么是爬虫

网络爬虫(Spider)是一种程序,它的主要目的是将互联网上的网页下载到本地并提取出相关数据。网络爬虫可以自动化的浏览网络中的信息,然后根据我们制定的规则下载和提取信息。

爬虫的原理

通过代码,模拟浏览器向服务器发送HTTP或者HTTPS请求,然后对服务器响应的结果进行处理,从中获取想要的数据。

课件上:可以把互联网比作一张大网,而爬虫(即网络爬虫) 便是在网上爬行的蜘蛛。 把网的节点比作一个个网页, 爬虫爬到这就相当于访问了该页面,获取了其信息 。可以把节点间的连线比作网页与网页之间的链接关系,蜘蛛通过一个节点后,可以顺着节点连线继续爬行到达下一个节点,即通过一个网页继续获取后续的网页,这样整个网的节点便可以被蜘蛛全部爬行到,网站的数据 就可以被抓取下来了。

爬虫三步走

  • 获取数据:发送请求并接收响应结果
  • 处理数据:对响应结果进行处理,筛选出有效数据
  • 存储数据:将有效数据存储起来

爬虫的危害

(1)性能骚扰

Web服务器默认接收人类访问,而受限于编写水平和目的,网络爬虫将会为 Web服务器带来巨大的资源开销。

(2)法律风险

服务器上的数据有产权归属,而网络爬虫获取数据后牟利将带来法律风险。

(3)隐私泄露

网络爬虫可能具备突破简单访问控制的能力,获得被保护数据从而泄露个人隐私。

爬虫的应用和作用
收集数据
爬虫最直接、 最常用的使用方法, 其运行速度极快而且不会因为做重复的事情就感觉到疲劳, 因此使用爬虫来获取大量的数据。

尽职调查
查看公司的情况,是否有偷奸耍滑,篡改数据和欺骗嫌疑人 可以让欺骗行为赤裸裸的暴露在阳光下

刷流量,秒杀和抢票等
如果爬虫隐蔽的很好,网站不能识别为爬虫,就会当成正常活动  爬虫也可以抢购商品和火车票

如何爬取静态网页和动态网页代码

静态网页通过requests库获取源码,用bs4库和re库进行解析。

import requests

url=’https://www.baidu.com’
html=requests.get(url).text

动态网页通过selenium库模拟浏览器行为,等到所有源码加载完毕后,获取源码,用bs4库和re库进行解析。

from selenium import webdriver

browser = webdriver.Chrome()
browser.get("http://www.taobao.com")
context = browser.page_source
print(context)

Robots协议

基本语法: #注释,*代表所有,/代表根目录

User-agent:*   #所有爬虫

Disallow:/        #不希望被访问的URL, /代表全部

举个栗子:

例如禁止百度收录本站:
User-agent: Baiduspider
Disallow: /

 

爬虫的分类以及有哪些策略

通用网络爬虫

比较适合为搜索引擎搜索广泛的主题,主要由搜索引擎或大型Web服务提供商使用。

  • 深度优先策略: 按照深度由低到高的顺序,依次访问下一级网页链接,直到无法再深入为止。
  • 广度优先策略: 按照网页内容目录层次的深浅来爬行,优先爬取较浅层次的页面。当同一层中的页面全部爬行完毕后,爬虫再深入下一层。

聚焦网络爬虫

聚焦网络爬虫又被称作主题网络爬虫,其最大的特点是只选择性地爬行与预设的主题相关的页面。

  • 基于内容评价的爬行策略: 该种策略将用户输入的查询词作为主题,包含查询词的页面被视为与主题相关的页面。
  • 基于链接结构评价的爬行策略: 该种策略将包含很多结构信息的半结构化文档Web页面用来评价链接的重要性,其中一种广泛使用的算法为PageRank算法。
  • 基于增强学习的爬行策略: 该种策略将增强学习引入聚焦爬虫,利用贝叶斯分类器对超链接进行分类,计算出每个链接的重要性,按照重要性决定链接的访问顺序。
  • 基于语境图的爬行策略: 该种策略通过建立语境图学习网页之间的相关度,计算当前页面到相关页面的距离,距离越近的页面中的链接优先访问。

增量式网络爬虫

增量式网络爬虫只对已下载网页采取增量式更新或只爬行新产生的及已经发生变化的网页。
常用的更新方法如下:

  • 统一更新法: 以相同的频率访问所有网页,不受网页本身的改变频率的影响。
  • 个体更新法: 根据个体网页的改变频率来决定重新访问各页面的频率。
  • 基于分类的更新法: 爬虫按照网页变化频率分为更新较快和更新较慢的网页类别,分别设定不同的频率来访问这两类网页。

深层网络爬虫

  • Web页面按照存在方式可以分为表层页面和深层页面两类。表层页面指以传统搜索引擎可以索引到的页面,深层页面为大部分内容无法通过静态链接获取,隐藏在搜索表单后的,需要用户提交关键词后才能获得的Web页面。
    深层爬虫的核心部分为表单填写,包含以下两种类型。
    基于领域知识的表单填写: 该种方法一般会维持一个本体库,通过语义分析来选取合适的关键词填写表单。
  • 基于网页结构分析的表单填写: 这种方法一般无领域知识或仅有有限的领域知识,将HTML网页表示为DOM树形式,将表单区分为单属性表单和多属性表单,分别进行处理,从中提取表单各字段值。

Requests库

Requests库的主要方法

最常用的requests.get(url,params=None,**kwargs)
param是提交给网页的参数(例如?id=...这样的构造url),kwargs是request方法的参数
requests会返回一个Response对象,包含了网页的信息
包括:

  • r.status_code   状态码
  • r.text                HTTP返回内容的字符串形式(页面内容)
  • r.encoding       从内容中分析出的响应内容的编码方式
  • r.content          HTTP响应内容的二进制形式

如何获取请求头

F12-network-选一个网络流点开,然后复制Headers里面的User-Agent
例如:Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36

获取网页的源代码框架

import requests


def getHtml(url):
    try:
        agent = {'user-agent': 'Mozilla/5.0'}
        html = requests.get(url, timeout=30, headers=agent)
        html.raise_for_status()
        html.encoding = html.apparent_encoding
        return html.text
    except:
        return 'error'


def printHtml(html):
    print(html)


if __name__ == "__main__":
    url = "http://www.baidu.com"
    printHtml(getHtml(url))
[/infobox]
下载图片(注意文件操作)
import requests
import os

url = 'http://pic16.nipic.com/20110824/6954443_222914449000_2.jpg'
root = r'c://'
path = root + url.split('/')[-1]

try:
    if not os.path.exists(root):
        os.mkdir(root)
    if not os.path.exists(path):
        r=requests.get(url)
        with open(path,'wb')as f:
            f.write(r.content)
            f.close()
            print('文件保存成功')
    else:
        print('文件保存失败')
except:
    print('爬取失败')

 

正则表达式-RE库

正则表达式是各种字符串操作的强大工具。Python 中的正则表达式可以使用 re 模块来访问,它是标准库的一部分。为了避免在处理正则表达式时出现混淆,我们一般使用原始字符串 r"expression"。

常用方法:

查找与匹配:re.match 、re.search 、re.findall、re.finditer方法

re.match 函数来确定字符串的开头是否匹配。如果匹配,match 返回表示匹配的对象,否则返回 None。
re.search 函数在字符串中的任何位置找到匹配的模式。
re.findall 函数返回一个与模式匹配的所有子串的列表。

re.finditer 函数和 re.findall 类似,不过它返回一个迭代器,而不是一个列表。迭代器必须进行遍历才能获取里面的元素值,而迭代器的遍历只能通过for in循环进行。

示例代码:

import re

pattern = r"spam"

if re.match(pattern, "eggspamsausagespam"):
   print("Match")
else:
   print("No match")

if re.search(pattern, "eggspamsausagespam"):
   print("Match")
else:
   print("No match")
    
print(re.findall(pattern, "eggspamsausagespam"))

输出:
No match
Match
['spam', 'spam']

关于正则表达式返回对象的方法

import re

pattern = r"pam"

match = re.search(pattern, "eggspamsausage")
if match:
   print(match.group()) #返回匹配的字符串的组
   print(match.start()) #返回第一个匹配的开始位置
   print(match.end())   #返回第一个匹配的结束位置
   print(match.span())  #元组形式返回开始与结束的位置

结果:
pam
4
7
(4, 7)

搜索与替换:re.sub

函数原型:re.sub(pattern确定替换范围, 新内容, 主串, max=最大替换数量)
返回值:替换后的函数值
举个栗子:

import re

str = "My name is Loen. Hi Loen."
pattern = r"Loen"
newstr = re.sub(pattern, "Amy", str)
print(newstr)

结果:My name is Amy. Hi Amy.

元字符

一般使用原始字符串表示r'something'

常用的元字符

元字符 含义 举个栗子
. 表示任何单个字符
[] 字符集,对单个字符给出取值范围 [abc]表示a、b、c,[a - z]表示a到z单个字符
[^] 非字符集,对单个字符给出排除范围 [^abc]表示非a或b或c的单个字符
* 前一个字符0次或无限次扩展 abc*表示ab、abc、abcc、abccc等
+ 前一个字符1次或无限次扩展 abc+表示abc、abcc、abccc等
前一个字符0次或1次扩展 abc?表示ab、abc
| 左右表达式任意一个 abc|def表示abc、def
{m} 扩展前一个字符m次 ab{2}c表示abbc
{m,n} 扩展前一个字符m次至n次(含n次) ab{1,2}c表示abc、abbc
^ 匹配字符串开头 ^abc表示abc且在一个字符串的开头
$ 匹配字符串结尾 $abc表示abc且在一个字符串的结尾
() 分组标记,内部只能使用|操作符 (abc)表示abc,(abc|def)表示abc、def
\d 数字,等价于[0 - 9]
\w 单个字符,等价于[A-Za-z0-9_]

分组

可以圆括号围绕正则表达式的一部分来创建组,一个组可以作为元字符的参数,如 * 和?。

可以调用 group(0) 或者 group() 返回整个匹配。

调用 group(n) ,n 要大于 0,返回匹配的第 n 个组。

groups() 返回所有匹配的分组。

beautifulsoup库

获取源代码

BeautifulSoup对象可以是现成的代码,也可以从文件里读取,也可以是requests库返回的内容
from bs4 import BeautifulSoup

soup1 = BeautifulSoup("<html>data</html>", "html.parser")
soup2 = BeautifulSoup("open(D://demo.html)", "html.parser")
import requests
from bs4 import BeautifulSoup
r = requests.get("url")
demo = r.text
soup = BeautifulSoap(demo,"html.parser")
print(soup.pretty)

BeautifulSoup类的基本元素

基本元素 说明
Tag 标签,最基本信息组织单元<> </>标记
Name 标签的名字,<tag>.name    例如<p>的名字是p
Attributes 标签的属性,字典属性组织,格式:<tag>.attrs
NavigableString 标签内非属性字符串,格式:<tag>.string
Comment 标签内的注释部分,一种特殊的Comment类型

 

基于BeautifulSoup库的遍历方法

标签树的遍历 说明
下行遍历
  • .contents:获取tag的所有儿子节点,返回列表类型
  • .children:与上面那个类似,迭代类型(只能用在for-in中),用于循环遍历
  • .descendants:迭代类型,包含所有子孙用于循环遍历
上行遍历
  • .parent:返回节点的父亲标签
  • .parents:返回祖辈标签(soup的父亲是None)

for parent in soup.Tag.parents:       这样进行遍历

平行遍历

(必须在同一个父亲节点下才能进行平行遍历)

  • .next_sibling:返回下一个平行节点标签
  • .previous_sibling:返回上一个平行节点标签
  • .next_siblings:迭代类型,返回后序所有平行节点标签
  • .previous_siblings:迭代类型,返回前边所有平行节点标签

for sibling in soup.Tag.(next/previous)_siblings:       这样进行遍历

基于BeautifulSoup库的内容查找方法

函数:<>.find_all(name,attrs,recursive,string,**kwargs)

返回:一个列表类型,存储查找的结果

缩写:由于find_all比较常用所以有缩写形式

  • <tag>(...)等价于<tag>.find_all(...)
  • soup(...)等价于soup.find_all(...)
参数 含义
name 检索名字为name的标签

<>.find_all('a')   检索a标签

<>.find_all(['a','b']) 检索a标签与b标签

attrs 对标签的属性值进行检索

<>.find_all('p','course')   检索class是course的p标签

<>.find_all(id='link')   检索id是link的标签

recursive 是否对子孙全部检索,默认为True
string 在<>...</>中字符串区域的检索字符串,常配合正则表达式库使用

selenium

selenium可以模拟真实浏览器,自动化测试工具,支持多种浏览器,爬虫中主要用来解决JavaScript渲染问题,为爬取动态网页做好准备。

selenium的安装

https://npm.taobao.org/mirrors/chromedriver/下载对应的chrome版本驱动然后放到python的Scipts目录下(不要看别的地方放在chrome安装目录下),否则大概率出现异常

File "C:\python\lib\site-packages\selenium\webdriver\common\service.py", line 83, in start
os.path.basename(self.path), self.start_error_message)
selenium.common.exceptions.WebDriverException: Message: 'chromedriver' executable needs to be in PATH. Please see https://sites.google.com/a/chromium.org/chromedriver/home

常见操作

模拟浏览器启动

模拟访问页面并打印源代码,然后关闭

from selenium import webdriver

browser = webdriver.Chrome()
browser.get('https://www.baidu.com')
print(browser.page_source)
browser.close()

查找元素

from selenium import webdriver
from selenium.webdriver.common.by import By

browser = webdriver.Chrome()

browser.get("http://www.taobao.com")
# 使用具体的方法查找
input_first = browser.find_element_by_id("q")  # 查找id
input_second = browser.find_element_by_css_selector("#q")  # css选择器
# 使用selenium.webdriver.common.by import By作通用方法
input_third = browser.find_element(By.ID, "q")
input_forth = browser.find_element(By.CSS_SELECTOR, '#q')

print(input_first)
print(input_second)
print(input_third)
print(input_forth)
# 结果全都是一样的

其它的查找方法:

find_element_by_name
find_element_by_id
find_element_by_css_selector
find_element_by_xpath
find_element_by_link_text
find_element_by_partial_link_text
find_element_by_tag_name
find_element_by_class_name

查找多个元素只需要把element改成elements即可,此处不再赘述。

搜索商品

from selenium import webdriver

import time

browser = webdriver.Chrome()
browser.get("http://www.taobao.com")
input_str = browser.find_element_by_id('q')
input_str.send_keys("ipad")
time.sleep(1)
input_str.clear()
input_str.send_keys("MakBook pro")
button = browser.find_element_by_class_name('btn-search')
button.click()

执行js

#将滚动条移动到页面的底部
js="var q=document.documentElement.scrollTop=100000"  
driver.execute_script(js)  
time.sleep(3)  
#将滚动条移动到页面的顶部  
js="var q=document.documentElement.scrollTop=0"  
driver.execute_script(js)  
time.sleep(3)  
#若要对页面中的内嵌窗口中的滚动条进行操作,要先定位到该内嵌窗口,在进行滚动条操作 
js="var q=document.getElementById('id').scrollTop=100000" 
driver.execute_script(js) 
time.sleep(3)

翻页

wait = WebDriverWait(browser, 20)
inputs = wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, '#PageContgopage')))
inputs.clear() #清空输入框
inputs.send_keys(2) #在输入框中输入2
submit = wait.until(EC.element_to_be_clickable((By.CSS_SELECTOR, '#PageCont > a.btn_link')))
submit.click() # 点击提交按钮
wait.until(EC.text_to_be_present_in_element((By.CSS_SELECTOR, '#PageCont > span.at'), ’2’)

实战操作例子:点我

总结(参考0.1光年的代码)

from selenium import webdriver
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.common.by import By
from selenium.webdriver.common.desired_capabilities import DesiredCapabilities
from selenium.webdriver import ActionChains


# 获取WebDriver对象
# driver=webdriver.Firefox()
# driver=webdriver.Chrome()
# driver=webdriver.PhantomJS()


# 设置请求头
dcap = dict(DesiredCapabilities.PHANTOMJS)
dcap["phantomjs.page.settings.userAgent"] = ("Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36")
driver = webdriver.PhantomJS(desired_capabilities=dcap)


# Cookie处理
# 添加cookie
cookies={
    'username':'xxx',
    'password':'xxx'
}
driver.add_cookie(cookie_dict=cookies)
driver.get('http://example.com')
# 获取cookie
driver.get_cookies()


# 发起GET请求(阻塞状态)
driver.get('https://www.sogou.com/')


# 获取当前页面的URL
url=driver.current_url
# 获取HTML源码
source=driver.page_source


# 刷新
driver.refresh()

# 截图
driver.save_screenshot('sougou.png')

# 前进
driver.forward()
# 后退
driver.back()

# 最大化窗口
driver.maximize_window()

# 关闭当前页面(如果是最后一个页面,则退出浏览器)
# driver.close()
# 退出浏览器
# driver.quit()


# 获取网页中的元素

# 查找单个元素
# 通用查找方法,by参数指定查找类型,value参数指定查找值
# By.ID
# By.CLASS_NAME
# By.XPATH
# By.CSS_SELECTOR
# By.LINK_TEXT
# By.PARTIAL_LINK_TEXT
# By.NAME
# By.TAG_NAME
element=driver.find_element(by=By.ID,value='id')

# 特定查找方法
element = driver.find_element_by_id('id')
element=driver.find_element_by_class_name('class')
element=driver.find_element_by_xpath('xpath')
element=driver.find_element_by_css_selector('css_selector')
element=driver.find_element_by_link_text('link_text')                   # 通过完整链接定位
element=driver.find_element_by_partial_link_text('partial_link_text')   # 通过部分链接定位
element=driver.find_element_by_name('name')         # 通过元素name标记定位
element=driver.find_element_by_tag_name('tag_name') # 通过元素名称定位

# 查找多个元素(同上,返回list)
# 通用查找方法
elements=driver.find_elements(by=By.XPATH,value='xpath')
# 特定查找方法
elements=driver.find_elements_by_id('id')


# 清空输入框中的内容
element.clear()

# 模拟向输入框输入文字
element.send_keys('Python')

# 模拟按下回车键
element.send_keys(Keys.RETURN)

# 模拟点击(要求element为可点击按钮)
element.click()

# 处理弹窗
alert_ele=driver.switch_to.alert
alert_ele.dismiss()

# 拖拽元素ActionChains(动作链)
source_element=driver.find_element(By.ID,'id')
target_element=driver.find_element(By.ID,'id')
action_chains=ActionChains(driver)
action_chains.drag_and_drop(source=source_element,target=target_element).perform()


# 设置页面加载超时时间,单位:秒
driver.set_page_load_timeout(30)

# 处理加载页面时的等待
# 方式一:执行命令等待的最长时间,单位:秒
driver.implicitly_wait(5)
# 方式二:休眠线程
import time
time.sleep(5)
# 方式三:显式等待
try:
    element=WebDriverWait(driver,timeout=10).until(
        EC.presence_of_all_elements_located((By.ID,'id'))
    )
finally:
    driver.quit()


# 执行JS脚本
js='window.scrollTo(0,document.body.scrollHeight);'  # 滚动到页面底部
driver.execute_script(js)
time.sleep(3)

Scrapy爬虫框架

框架结构图

Engine引擎, 用来处理整个系统的数据流处理, 触发事务, 是整个框架的核心。
Scheduler调度器, 用来接受引擎发过来的请求并加入队列中, 并在引擎再次请求的时候提供给引擎。
Downloader下载器, 用于下载网页内容, 并将网页内容返回给蜘蛛。
Spiders蜘蛛, 其内定义了爬取的逻辑和网页的解析规则, 它主要负责解析响应并生成提取结果和新的请求
Item Pipeline项目管道, 负责处理由蜘蛛从网页中抽取的项目, 它的主要任务是清洗、 验证和存储数据

运行流程:

  1. 引擎从调度器中取出一个链接(URL)用于接下来的抓取
  2. 引擎把URL封装成一个请求(Request)传给下载器
  3. 下载器把资源下载下来,并封装成应答包(Response)
  4. 爬虫解析Response
  5. 解析出实体(Item),则交给实体管道进行进一步的处理
  6. 解析出的是链接(URL),则把URL交给调度器等待抓取

常用操作

  1. 创建一个Scrapy爬虫工程:scrapy stratproject<工程名字>
  2. 在工程中产生一个Scrapy:scrapy genspider <爬虫名字><网站域名>
  3. 配置产生的spider爬虫:获取、解析网页内容
  4. 运行爬虫以获取网页:scrapy crawl<爬虫名字>
  5. 爬虫结果保存到指定文件:scrapy crawl<爬虫名字> -o<文件名.格式>

 

点赞

发表评论

电子邮件地址不会被公开。必填项已用 * 标注

19 + 17 =