前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >使用Python去爬虫

使用Python去爬虫

作者头像
一只羊
发布2019-07-27 18:49:32
1.5K0
发布2019-07-27 18:49:32
举报
文章被收录于专栏:生信了生信了

本文是笔者日常使用Python进行爬虫的简要记录。

爬虫,简单说就是规模化地采集网页信息,因为网络像一张网,而爬虫做的事就像一只蜘蛛在网上爬,所以爬虫英文名就是spider。

爬虫可以做很多事情,比如抓取网页上的表格,下载歌曲、下载电影、模拟登录网站等等,基本上都是和网页相关的。当然,现在很多所谓的”手机爬虫“也出现了,原理类似。我们今天只说PC端的网页爬虫。

讲爬虫的技术文章数不胜数,很多编程语言也有现成的模块。笔者几乎只用Python,也只会用Python来进行爬虫,所以本文是讲如何用Python来进行爬虫。写这篇文章一是分享,二是把常用代码记录下来,方便自己查找。本文篇幅较长,主要分为以下五个部分:

  • 理论基础
  • 实现方法
  • 注意点
  • 难点
  • 小结

理论基础

爬虫,大多数时候是和网页打交道,所以和网页相关的常用技术多少要了解掌握。如:

  • HTTP协议。主要是了解HTTP协议头。GET、POST方法等。常涉及到urllib、urllib2、requests模块。
  • Cookie。一种服务器端记录客户端连接情况的工具。常涉及到cookielib模块。
  • HTML。早期静态网页几乎都是HTML文本。
  • Javascript。最流行的动态网页编程语言。可能会用到pyv8模块。
  • CSS。讲如何布局、渲染网页的。
  • AJAX。如何延迟显示网页内容。常涉及到json模块。
  • DOM。抽象化的网页结构。常涉及到bs4(Beautiful Soup)、lxml模块。
  • css-selector/xpath。如何定位网页元素。常涉及到bs4(Beautiful Soup)、lxml模块。
  • 正则表达式。规则化地抽取文本。常涉及到re、bs4(Beautiful Soup)、lxml模块。

基本上这些都是要了解的。其实,谷歌浏览器Chrome提供的开发者工具就是一个强有力的辅助学习工具。可以借助它快速熟悉上述技术。

实现方法

本着实用、简洁的原则。笔者将自己常用的代码整理如下:

只用到GET方法,没有什么复杂的情况。
代码语言:javascript
复制
# urllib模块可以很方便地实现 GET 方法。
import urllib

# eg: res = urllib.urlopen("http://youku.com")
res = urllib.urlopen("<your_url>") 
html = res.read()  # 像读取文件一样读取网页内容
info = res.info()  # 返回的header信息
res.close()   # 像关闭文件一样关闭网络连接
需要用到POST方法。
代码语言:javascript
复制
# urllib2模块可以方便地实现 POST 方法。
# 假设参数是 your_url?user=hxj5hxj5&passwd=testtest
import urllib, urllib2

headers = {  # 请求头。根据情况调整,一般而言,最重要的是'User-Agent'一项。
  'Accept': 'application/json',
  'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/537.36'
}
paras = {  # 请求参数。根据情况调整
  'user': 'hxj5hxj5',
  'passwd': 'testtest'
}
paraData = urllib.urlencode(paras)
req = urllib2.Request(url="<your_url>", data=paraData, headers=headers)
res = urllib2.urlopen(req) 
html = res.read()  # 像读取文件一样读取网页内容
res.close()   # 像关闭文件一样关闭网络连接
需要用到cookie
代码语言:javascript
复制
import urllib2, cookielib

# cookielib模块可以很方便地操作cookie。
# 假设参数是 your_url?user=hxj5hxj5&passwd=testtest
cj = cookielib.CookieJar()
# 创建能自动处理cookie的实例,通过它来打开请求
opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(cj)) 
# 如果是GET请求
res = opener.open("<your_url>")
# 如果是POST请求
# req = urllib2.Request(...)
# res = opener.open(req)
html = res.read()
res.close()
获取特定元素的内容

通过BeautifulSoup来实现

代码语言:javascript
复制
import urllib
from bs4 import BeautifulSoup

res = urllib.urlopen("<your_url>") 
html = res.read()
res.close()

soup = BeautifulSoup(html, 'lxml')
taga = soup.select("a")  # 根据CSS-selector来定位元素,返回列表
for a in taga:
  print a["href"]  # 打印节点的属性
  print a.text     # 打印节点的内容

通过 re 来实现 re模块是Python自带的创建、解析正则表达式的模块。

代码语言:javascript
复制
import urllib
import re

res = urllib.urlopen("<your_url>") 
html = res.read()
res.close()

pat = re.compile(r'''<a href=(.*?)>''')  # 创建正则表达式
result = pat.findall(html)  # 返回所有符合条件的元素
for item in result:
  print item   # 打印元素内容
下载数据
代码语言:javascript
复制
# 使用urllib模块中的urlretrieve函数可以很方便地下载数据
# 假设要下载一张图片
import urllib

urllib.urlretrieve("http://just4test.cn/path/to/spider.jpg", "spider.jpg")

注意以上所说的几种功能都可以通过其他模块或方法实现,这里列出的只是笔者常用的而已

注意点

爬虫其实是一个很琐碎繁复的过程,有很多细节需要注意。下面列出一些笔者常遇到的问题。

数据被压缩过

有时候服务器端会将数据压缩后再传输到客户端,所以我们需要对压缩过的数据进行解压。常用的压缩方式就是gzip压缩。以此为例:

代码语言:javascript
复制
import urllib
import gzip
from StringIO import StringIO

def ungzip(data):
  buf = StringIO(data)
  f = gzip.GzipFile(fileobj=buf)
  return f.read()

res = urllib.urlopen("<your_url>") 
html = res.read()
content = res.info().get('Content-Encoding')
res.close()
if content == "gzip":
  html = ungzip(html)
数据编码

Python中的字符串编码一直是很让人头疼的,爬虫中就经常会遇到这样的问题。 假设网页不是utf8编码(比如gbk编码)的,而你想要保持utf8编码,那么就需要进行编码的转换。 首先得判断网页编码格式,常用chardet模块实现。

代码语言:javascript
复制
#!/usr/bin/env python
#-*-coding:utf8-*-

import urllib
import chardet

res = urllib.urlopen("<your_url>") 
html = res.read()
res.close()

encoding = chardet.detect(html)['encoding']
if encoding != 'utf8':  # 以utf8为例
  html = html.decode(encoding)
数据是json格式的
代码语言:javascript
复制
import urllib
import json

res = urllib.urlopen("<your_url>") 
html = res.read()
isJson = 'json' in res.info().get('Content-Type')
res.close()

if isJson:
  data = json.loads(html)
整站抓取

如果是一个要实现大规模抓取任务的爬虫,最好是使用成熟的爬虫框架如Scrapy。但是好在笔者目前还没有碰到过这种规模的任务,所以也没有用过Scrapy。下面只是从原理上大概探讨一下这种情形。

比较常见的比如抓取一个网站上的所有图片。如果把网站看成一棵树,而该网站的各个页面是树的各个节点,那么抓取所有图片就需要遍历所有节点(页面),并在每个节点(页面)上抓取该页面上的所有图片。那么可以用BFS(广度优先搜索)或者DFS(深度优先搜索)算法。 以BFS为例:

代码语言:javascript
复制
import urllib
from bs4 import BeautifulSoup

def spider(url, depth):
  if depth > MAX_DEPTH:  # 到达最大深度后就不再继续爬取了
    return
  res = urllib.urlopen(url) 
  html = res.read()
  res.close()
  soup = BeautifulSoup(html, 'lxml')
  # 下载图片
  pics = soup.select(a[href$=".jpg"])  # 假设只选取以jpg结尾的图片
  for p in pics:
    urllib.urlretrieve(p, str(picNum) + ".jpg")
    picNum += 1
  # 抓取新的页面链接
  theUrls = soup.select(a[href$=".html"])  # href属性以html结尾的所有a标签
  newUrls = set(theUrls) - oldUrls   # 如果在oldUrl中出现过,就排除掉 
  oldUrls.update(newUrls)  # 更新已有链接集合
  for nu in newUrls:
    spider(nu, depth + 1)  # 对新的页面链接继续爬取

picNum = 0
MAX_DEPTH = 10
initUrl = "http://just4test.cn/"  # 初始页面
oldUrls = set([initUrl])
spider(initUrl, 0)  # 从深度0开始爬取,到达最大深度后停止

难点

爬虫的难点主要是如何绕过反爬虫机制。

限制频繁访问

为了减少服务器端的访问压力,一般都不会允许频繁访问网站(即不允许频繁发送请求)。为了解决这一点,所以最好能随机休息/暂停。

代码语言:javascript
复制
import urllib
import time
from random import randint

def randSleep(s=0, e=1):
  t = randint(s * 1000, e * 1000) / 1000.0
  time.sleep(t)
  return t

for url in allUrls:
  res = urllib.urlopen(url) 
  html = res.read()
  res.close()
  randSleep()
限制ip

有些服务器在判明是爬虫在爬取数据后,会封ip。这时候只能换一个ip。最好是能找到代理服务器,有一个ip池。封了一个ip,立即切换到另一个ip。

检查请求头

服务器端检查请求头,如果发现异常,就阻止请求。最常见的检查'User-Agent'一项,看是否是正常的真实的浏览器。或者检查'Referer'一项是否正常。这些都可以通过Chrome的开发者工具获取真实值后进行伪装。

当获取到相应值之后,可以一开始就在请求头中指定,也可以之后添加。 如果在一开始就指定,像这样:

代码语言:javascript
复制
import urllib, urllib2

headers = {  # 请求头。
  'Referer': 'http://just4test.cn/page1.html',
  'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/537.36'
}
paras = {  # 请求参数。根据情况调整
  'user': 'hxj5hxj5',
  'passwd': 'testtest'
}
paraData = urllib.urlencode(paras)
req = urllib2.Request("http://just4test.cn/page2.html", data=paraData, headers=headers)
res = urllib2.urlopen(req) 
html = res.read() 
res.close()   

或者之后添加

代码语言:javascript
复制
import urllib, urllib2

headers = {  # 请求头。
  'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/537.36'
}
paras = {  # 请求参数。根据情况调整
  'user': 'hxj5hxj5',
  'passwd': 'testtest'
}
paraData = urllib.urlencode(paras)
req = urllib2.Request(url="http://just4test.cn/page2.html", data=paraData, headers=headers)
req.add_header('Referer', 'http://just4test.cn/page1.html') # 添加信息
res = urllib2.urlopen(req) 
html = res.read() 
res.close()  
检查cookie

如上所说,可以使用cooklib模块自动处理cookie。但是当知道了具体的cookie值的时候,也可以直接将cookie添加到请求头中。

代码语言:javascript
复制
import urllib, urllib2

headers = {  # 请求头。
  'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/537.36'
}
paras = {  # 请求参数。根据情况调整
  'user': 'hxj5hxj5',
  'passwd': 'testtest'
}
paraData = urllib.urlencode(paras)
req = urllib2.Request(url="http://just4test.cn/page2.html", data=paraData, headers=headers)
req.add_header('Cookie', 'USER_ID=hxj5hxj5') # 添加cookie
res = urllib2.urlopen(req) 
html = res.read() 
res.close()  
复杂参数

有些网页请求的参数特别复杂,比如百度搜索'python'时的请求链接是"https://www.baidu.com/s?ie=utf-8&f=8&rsv_bp=1&rsv_idx=1&tn=baidu&wd=python&oq=%25"(后面还有一长串),很多参数一眼看上去不知道是什么意思,也无从获取。这个时候写爬虫就很麻烦,因为你没法知道参数该用什么值。

遇到这种情况,一般有三种办法:

一是利用 Chrome 的开发者工具提供的设置断点等功能进行手动调试,一般请求链接中的参数还都是可以从 js 文件运行过程中得到的,所以手动调试有希望能获取参数值

二是利用诸如 v8 引擎(Python中有 pyv8 模块)执行 js 代码,从而获取参数值

三是利用 selenium 之类的工具绕过获取参数值这一步

人机验证

一旦碰到这种情况,以笔者目前的经验和水平,大多是不能靠基础模块和方法解决的。只能选择 selenium 这种工具来变通。

验证码 简单验证码可以直接用 OCR 工具破解,复杂一点的需要先去噪,然后建模训练进行破解。再复杂的就只能放弃或者人工输入验证码后让爬虫程序继续。

拖拽(点击)图形 如微博登录、12306购票都是这一类的。大多数也是靠 selenium 去想办法。

容错机制

爬虫要特别注意容错,不然很容易出现运行中途出错退出的情况。比如,网速不好,连接暂时丢失导致报错、字符串不规范(举一个例子,本来预期应该是有字符的地方是空的)从而导致出错、本来表格中预期有5个<li>元素的,结果只有4个从而报错等等。爬虫太繁琐了,很多细节都容易出错。所以一定要有容错机制。 比如我们可以最多尝试3次,3次都失败了就退出:

代码语言:javascript
复制
import urllib
import sys

maxTry = 3
isOK = 0
for _ in range(maxTry):
  try:
    res = urllib.urlopen(url) 
    html = res.read()
    res.close()
  except Exception as e:   # 最好指定特定类型的exception
    print str(e)
  else:
    isOK = 1
    break
if not isOK:
  print "urlopen failed."
  sys.exit(1)
selenium

PhantomJS 以及 selenium 这一类的工具都可以用来进行浏览器自动化测试,就相当于你在操纵一个真实的浏览器。笔者只用过 selenium。它们显得笨重,但是功能的确强大,可以解决很多复杂的爬虫问题。网上有很多教程,其主要用法如下:

代码语言:javascript
复制
from selenium import webdriver

browser = webdriver.Chrome()
browser.implicitly_wait(10)  # 设置默认等待时间
browser.get("<your_url>")  # 打开网页
print browser.page_source  # 打印网页源代码
# 查找特定元素
tgtEle = browser.find_elements_by_css_selector('a') # 或者 browser.find_elements_by_xpath()
for e in tgtEle:
  print e.text
  print e.get_attribute('href')
tgtEle[0].click()  # 点击元素
# 执行js代码
browser.execute_script('window.scrollTo(0, document.body.scrollHeight)')
# cookie操作
get_cookies()
delete_all_cookes()
add_cookie({'name':'test', 'value':'123'})
# 选项卡操作(以切换选项卡为例)
browser.switch_to_window(browser.window_handles[1])
# 或者browser.switch_to.window(browser.window_handles[1])
browser.close()  # 关闭浏览器

小结

在Python中,爬虫相关的模块有不少,如果是日常简单的任务,用urllib,requests这些基础模块就够用了。但是如果是复杂的或者规模很大的爬虫,最好使用Scrapy之类的框架。最后要说的就是 selenium 是我们遇到困难时的好帮手。

本文是笔者使用Python进行爬虫的一个简要记录,仅供大家参考。由于只是一个业余使用者,所以文中肯定有不少概念和代码使用上的错误,希望大家不吝指教。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-07-19,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 生信了 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 理论基础
  • 实现方法
  • 注意点
  • 难点
  • 小结
相关产品与服务
验证码
腾讯云新一代行为验证码(Captcha),基于十道安全栅栏, 为网页、App、小程序开发者打造立体、全面的人机验证。最大程度保护注册登录、活动秒杀、点赞发帖、数据保护等各大场景下业务安全的同时,提供更精细化的用户体验。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档