爬虫系列学习笔记(一)

Posted on Posted in python

用 python 操作数据库

一、PyMongo 安装和使用

  1. mongodb 安装

mac

brew update
brew install mongodb

#启动MongoDB
mongod --config /usr/local/etc/mongod.conf

linux - ubuntu

sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv EA312927

echo "deb http://repo.mongodb.org/apt/ubuntu trusty/mongodb-org/3.2 multiverse" | sudo tee /etc/apt/sources.list.d/mongodb-org-3.2.list

sudo apt-get update

sudo apt-get install -y mongodb-org

#启动MongoDB
sudo service mongod start

Windows

1) 下载并安装:https://www.mongodb.com/download-center?jmp=nav#community

2) 创建文件夹用来存放数据文件:D:\MongoDB\DB

3) 在MongoDB的安装文件夹中,按住Shift键并点击鼠标右键,选择“在此处打开命令窗口”,然后输入以下代码启动MongoDB:

mongod.exe --dbpath D:\MongoDB\DB
  1. 图形化图形化管理工具-RoboMongo

下载地址为:https://robomongo.org/download

  1. PyMongo

1) pip安装

pip install pymongo

如果pip安装失败,到http://www.lfd.uci.edu/~gohlke/pythonlibs/下载whl包到本地,然后 pip install whl包

2) 使用easy_install 安装

easy_install pymongo

注意事项

sudo apt-get install build-essential python-dev # ubuntu系列
sudo yum install gcc python-devel # redhat 系列
  1. pymongo 的使用
#!/usr/bin/env python  
# -*- coding: utf-8 -*-  
# pip install pymongo  
from pymongo import MongoClient  
# 第一种形式:参数形式  
# conn = MongoClient() # 默认什么都不写,是127.0.0.1,27017  
# conn = MongoClient(host = '192.168.197.133', port = 27017)  
# 第二种形式:URI 形式  
conn = MongoClient('mongodb://192.168.197.133:27017')  
database = conn.person_info  
col = database.people  
  
info_dict = {'name':'name01', 'age':20, 'sex':'male', 'salary':9999999} # 插入数据,因为是字典,所以是无序的  
col.insert(info_dict)  
info_dict = {'name':'name02', 'age':28, 'sex':'unknown', 'salary':9999999} # 插入数据,因为是字典,所以是无序的  
col.insert(info_dict)  
  
# fname = col.find() # 当不使用条件的时候,就会查询所有人的信息  
fname = col.find({'name':'name'}) # 查询操作  
for each in fname:  
    print(each['salary'])  
  
col.update({'name':'name'},{'$set':{'age':50, 'sex':'abc'}})  
col.delete_many({'name':'name'}) #删除所有的,如果是delete_one,只删除第一条  

二、MongoEngine 的安装和使用

  1. MongoEngine 安装
pip install mongoengine
  1. 使用
#!/usr/bin/env python  
# -*- coding: utf-8 -*-  
# pip install mongoengine  
from mongoengine import *  
  
#connect('jikexueyuan') # 默认本地localhost  
connect('jikexueyuan',host='192.168.197.133',port=27017) # 连接数据库  
  
# orm,对象关系映射  
class People(Document):  
    name = StringField(required=True) # 字符串的类型,required=True表示是必须要提供的  
    age = IntField(required=True) # 整形  
    sex = StringField(required=True)  
    salary = IntField() # 没有required=True,可以有,可以没有  
# 插入修改数据  
kingname = People(name='meizhi', age=29, sex='female')  
kingname.salary = 123456 # 可以像上面那样写到一起,也可以单独写一个。也可以作为修改某项  
kingname.save()  
  
## 查询数据  
for each in People.objects:  
    print(each.name) # 打印出所有的名字  
  
# 条件查询  
namelist = People.objects(name="meizhi")  
print(namelist) # 打印出People的对象  
for i in namelist:  
    print i.name  
  
namelist.delete() # 删除kingname  
for i in namelist: # 删除多个  
    namelist.delete()  

三、Redis-Py 的安装及使用

  1. Redis 安装

Mac

使用homebrew安装:

brew update
brew install redis

#运行Redis
redis-server

Ubuntu

wget http://download.redis.io/releases/redis-3.2.1.tar.gz
tar xzf redis-3.2.1.tar.gz
cd redis-3.2.1
make

#运行解压以后的文件夹下面的src文件夹中的redis-server文件启动redis服务
src/redis-server

Windows

Redis没有Windows的官方安装包,但是有第三方的安装包。

下载安装文件zip包:https://github.com/MSOpenTech/redis/releases

解压以后的文件夹,使用命令行运行里面的redis-server即可。

  1. Redis-Py 安装
pip install redis
  1. Redis-Py 使用
#!/usr/bin/env python  
# -*- coding: utf-8 -*-  
# pip install redis  
import redis  
  
r = redis.StrictRedis(host='192.168.197.133', port=6379)  
  
r.set('key', 'value') # set 设置信息  
print(r.get('key')) # 像字典一样读写信息  
r.set('name','username')  
print(r.get('name'))  
r.append('name',' is a super man') # 在已有的信息后面添加字符串  
print(r.get('name'))  
print(r.keys()) # 查看现在一共有多少的key  
r.delete('name') # 删除key  
print(r.keys()) # 查看现在一共有多少的key 

连接池: 由于创建连接是非常消耗系统资源的。所以不应该频繁的创建/断开到redis数据库的连接。于是需要使用连接池来管理Redis 的连接。Redis-py已经有一个ConnectionPool类来帮我们完成了这个事情。我们要做得只是创建连接池,然后使用。其他的一切,Redis-py会自动帮我们完成。


#!/usr/bin/env python  
# -*- coding: utf-8 -*-  
# pip install redis  
import redis  
  
connection_pool = redis.ConnectionPool(host = '192.168.197.133', port = 6379, db = 0)  
  
def getValue(key):  
    server = redis.Redis(connection_pool=connection_pool) #注意这里使用的是redis.Redis  
    response = server.get(key)  
    return response  
  
def setValue(key, value):  
    server = redis.Redis(connection_pool=connection_pool)  
    server.set(key, value)  
  
setValue('a','avalue')  
print getValue('a')  

redis.Redis()和redis.StrictRedis()的区别

在上面的代码中,我第一段代码使用了redis.StrictRedis, 而在第二段代码中使用了redis.Redis

他们的区别:redis.StrictRedis这个类,它是严格根据Redis的命令语法来实现。而redis.Redis这个类是redis.StrictRedis的子类,它复写了一些命令来保证向后兼容老的Redis-py版本,如果你不需要实现向后兼容的话,就可以使用redis.StrictRedis, 否则,就使用redis.Redis 。

正则表达式

基本符号

点号"."

  • 代表任意除了\n意外的字符
  • 一个点号代表一个字符
  • abcd -> a..b
  • 点号就是一个占位符

星号"*"

  • 代表他前面的子表达式0次到多次
  • abcde -> a.*de 代表多个.
  • abcde -> ab.*e
  • abceeeeeeeee -> abce* 代表多个e
  • abc -> abcx* 代表0个x

问号"?"

  • 代表前面的子表达式0次或者1次
  • abcdefghijk -> abc.?jk 代表d一个字母
  • abcdefghijk -> abcdefx?ghijk 代表0个x

转义字符""

  • 不能歹毒使用
  • 让有特殊意义的字符编程普通字符:"\*"代表普通的星号
  • 让普通的字符编程特殊的字符:"\d"代表数字

应用举例


#!/usr/bin/env python  
# -*- coding: utf-8 -*-  
import re  
sentence_1 = "liuhonghe"  
sentence_2 = "我的密码是123456789,你的密码是98765432"  
sentence_3 = "我是liuhonghe,我的微博是:12345678,QQ密码是:890abcd,银行卡密码是:654321,Github密码是:7777love8888"  
  
result_1 = re.findall('liu(....)he',sentence_1) # 括号的意思,就是提取括号里面的内容  
print(result_1)  
  
result_2 = re.findall('(\d+)', sentence_2) # 匹配数字  
print result_2  
  
result_3_1 = re.findall(':(.*),',sentence_3)  
result_3_2 = re.findall(':(.*?),',sentence_3)  
print result_3_1  
print result_3_2  
  
#with open('text.txt',encoding='utf-8') as f: # python3 加个encoding  
with open('text.txt') as f:  
    text = f.read()  
    print(text)  
    #result_4 = re.findall(':(.*?\n.*?),',text) # 两种一样的效果  
    result_4 = re.findall(':(.*?),',text,re.S)  
    print result_4  

正则表达式

#!/usr/bin/env python  
# -*- coding: utf-8 -*-  
import re  
# findall  
example_text = "我是kingname, 我的微博账号是:kingname, 密码是:12345678, QQ账号是:99999, 密码是:890abcd, 银行卡账号是:000001, 密码是:654321, Github账号是:99999@qq.com, 密码是:7777love8888, 请记住他们。"  
  
user_password = re.findall('账号是:(.*?), 密码是:(.*?),', example_text)  
print(user_password)  
for each in user_password:  
    print('账号: {}, 密码: {}'.format(each[0], each[1]))  
  
# search  
# search方法可以返回第一个满足要求的字符串。一旦找到符合要求的内容,它就会停止查找。  
# search的结果是一个正则表达式的对象,需要通过.group()这个方法来获取里面的值  
example_text = '我是kingname, 我的微博账号是:kingname, 密码是:12345678, QQ账号是:99999, 密码是:890abcd, 银行卡账号是:000001, 密码是:654321, Github账号是:99999@qq.com, 密码是:7777love8888, 请记住他们。'  
user_password = re.search('账号是:(.*?), 密码是:(.*?),', example_text) # 根据括号的个数确定group的个数  
print(user_password)  
print(user_password.group())  
print(user_password.group(1))  
print(user_password.group(2))  
  
# print(user_password.group(3)) #此处会报错,因为没有group(3)  
  
# 技巧  
# 不需要compile  
# 先抓大再抓小  
  
example_text = '我是kingname, 我的微博账号是:kingname, 密码是:12345678, QQ账号是:99999, 密码是:890abcd, 银行卡账号是:000001, 密码是:654321, Github账号是:99999@qq.com, 密码是:7777love8888, 请记住他们。'  
# 虽然效果一样,但是不要使用compile, python源码里compile流程是重复执行的  
new_pattern = re.compile('账号是:(.*?), 密码是:(.*?),', re.S)  
user_pass = re.findall(new_pattern, example_text)  
print(user_pass)  
  
# 先大后小  
example_text = ''' 
有效用户: 
姓名: 张三 
姓名: 李四 
姓名: 王五 
无效用户: 
姓名: 死人 
姓名: 僵尸 
'''  
# user = re.findall('姓名: (.*?)\n', example_text)  
# print(user)  
  
user_big = re.findall('有效用户(.*?)无效用户', example_text, re.S)  
print('user_big 的值为: {}'.format(user_big))  
  
user_useful = re.findall('姓名: (.*?)\n', user_big[0])  
print(user_useful)  

网页内容解析

  1. requests
#!/usr/bin/env python  
# -*- coding: utf-8 -*-  
import requests  
  
# get 和 post 方法  
#html = requests.get('https://movie.douban.com/top250').content  
#print html  
  
f = open('abc.jpg','rb')  
data = {'smfile':f}  
content = requests.post('https://sm.ms/api/upload', files = data).content  
print content  
f.close()  
  1. xpath

获取文本信息 :

//标签1[@属性1="属性值1"]/标签2[@属性2="属性2"]/.../text()  

获取属性值 :

//标签1[@属性1="属性值1"]/标签2[@属性2="属性2"]/.../@属性n 

语法举例

#!/usr/bin/env python  
# -*- coding: utf-8 -*-  
# pip install lxml  
import lxml.html  
html = ''' 
<html> 
  <head> 
    <title>测试</title> 
  </head> 
  <body> 
    <div class="useful"> 
      <ul id="useful"> 
        <li class="info">我需要的信息1</li> 
        <li class="info">我需要的信息2</li> 
        <li class="info">我需要的信息3</li> 
        <li>我需要的信息4</li> 
      </ul> 
     </div> 
     <div class="useless"> 
       <ul id="useless"> 
         <li class="info">垃圾1</li> 
         <li class="info">垃圾2</li> 
       </ul> 
     </div> 
     <div id="url"> 
        <a href="http://www.abc.com">abc</a> 
        <a href="http://www.bcd.com" title="bcd">def</a> 
     </div> 
  </body> 
</html> 
'''  
  
selector = lxml.html.fromstring(html) # 处理源代码,以便使用xpath  
# 提取文本  
# content = selector.xpath('//ul[@id="useful"]/li[@class="info"]/text()') # 获取id为useful的ul,获取class为info的li,获取文本  
# content = selector.xpath('//ul[@id="useful"]/li/text()') # 获取所有的li  
content_0 = selector.xpath('//ul[@id="useful"]')[0] # 这是个列表,如果有多个useful的ul标签,使用这种情况  
content = content_0.xpath('li/text()') # 当xpath使用的是上一个xpath的结果,就不需写两个/  
print content  
for i in content:  
    print(i)  
  
# 提取属性  
link = selector.xpath('//div[@id="url"]/a/@href')  
print link  
  
# 获取a标签的title信息  
title = selector.xpath('//a/@title')  
print title  

xpath 技巧

  • 以相同的字符开头:starts-with
  • 标签套标签:string(.)
#!/usr/bin/env python  
# -*- coding: utf-8 -*-  
import lxml.html  
  
html1 = ''' 
<html> 
<head> 
<title>标题</title> 
</head> 
<body> 
    <div id="content-1">内容1</div> 
    <div id="content-1">内容2</div> 
    <div id="contentabc">内容3</div> 
    <div id="useless">内容4</div> 
</body> 
</html> 
'''  
html2 = ''' 
<html> 
<head> 
<title>标题</title> 
</head> 
<body> 
<div id="test"> 
    a 
    <span id="b"> 
        b 
        <ul>c 
            <li>d</li> 
        </ul> 
    </span> 
    e 
</div> 
</body> 
</html> 
'''  
selector = lxml.html.fromstring(html1)  
content = selector.xpath('//div[starts-with(@id,"content")]/text()')  
for each in content:  
    print(each)  
  
selector1 = lxml.html.fromstring(html2)  
data = selector1.xpath('//div[@id="test"]')[0]  
info = data.xpath("string(.)")  
info = info.replace('\n','').replace(' ','')  
print info 
  1. beatifull soup4
# pip install beautifulsoup4  
from bs4 import BeautifulSoup  
import requests  
import re  
''' 
<html> 
  <head> 
    <title>测试</title> 
  </head> 
  <body> 
    <div class="useful"> 
      <ul> 
        <li class="info">我需要的信息1</li> 
        <li class="test">我需要的信息2</li> 
        <li class="iamstrange">我需要的信息3</li> 
      </ul> 
     </div> 
     <div class="useless"> 
       <ul> 
         <li class="info">垃圾1</li> 
         <li class="info">垃圾2</li> 
       </ul> 
     </div> 
  </body> 
</html> 
'''  
html = requests.get('http://exercise.kingname.info/exercise_bs_1.html').text  
# print(html)  
  
soup = BeautifulSoup(html, 'html.parser') # 初始化  
  
useful = soup.find(class_ = 'useful') # 这里的class有下划线,  
# all_content = soup.find_all('li') # 所有满足条件的,都打印出来  
all_content = useful.find_all('li')  
for li in all_content:  
    print(li.text) # 获取文本  
    print(li.string) # 效果和上面的text是一样的  
    print(li['class']) # 获取属性值  
print('#'*20)  
# 和正则配合使用  
#content = soup.find_all(text=re.compile('我'))  
content = soup.find_all(class_ = re.compile('ia'))  
for each in content:  
    print(each.string)  
  1. 多线程爬虫
#!/usr/bin/env python  
# -*- coding: utf-8 -*-  
from multiprocessing.dummy import Pool as ThreadPool # 当不写dummy的时候,是多进程  
import threading  
import requests  
import time  
  
  
def getsource(url): # 访问url  
    global hasViewed #修改全局变量,需要使用global关键字  
    print(url)  
    html = requests.get(url)  
    hasViewed.append(url)  
  
def threadGetSource(url): # 调用getsource,仅仅是在threading上使用。初始化线程,并启动一个线程  
    print(url)  
    x = threading.Thread(target=getsource, args=(url,)) #这里的args=(url,) 逗号是必须的,因为加了逗号才是只有一个元素的元组  
    x.start()  
  
urls = []  
hasViewed = []  
  
for i in range(1, 21): # 生成网页链接,报存在urls中  
    newpage = 'http://tieba.baidu.com/p/3522395718?pn={}'.format(i)  
    urls.append(newpage)  
  
#==========单线程=====================================  
time1 = time.time()  
hasViewed = []  
for i in urls:  
    getsource(i)  
print('单线程耗时:{}'.format(time.time() - time1))  
  
#============Python multiprocessing.dumpy多线程=======  
pool = ThreadPool(20) # 线程池,20个  
time3 = time.time()  
hasViewed = []  
results = pool.map(getsource, urls) # map函数,将所有urls列表,传入到getsource中  
pool.close()  
pool.join() # 全部执行完毕,才继续运行  
print('并行耗时:{}'.format(time.time() - time3))  
  
#======Python threading 多线程=======================  
time5 = time.time()  
hasViewed = [] # 这个列表会在上面的函数getsource中使用  
[threadGetSource(url) for url in urls] # 将每个url提交给threadGetSource  
while not len(hasViewed) == 20:  
    pass  
print('thread并行耗时: {}'.format(time.time() - time5))  
  1. 爬虫算法

在游戏里面我们会找NPC来接任务。有的同学喜欢一次只领取一个任务,把这个任务做完,再去领下一个任务,这就叫做深度优先算法。另外一些同学他们喜欢先把所有的任务一次性接玩,然后再去慢慢完成,最后再一次性的把任务奖励都领取了。这就叫做广度优先算法。

爬取动态加载网页

  1. json解析与生成
  • json格式的数据是字符串
  • python的json库用来做json格式字符串的解析和生成
  • 解析:json.loads(json格式字符串,转成字典)
  • 生成:json.dumps(python字典,转成字符串)
# -*- coding: utf-8 -*-  
import json  
person_dict = {'basic_info':  
              {'name': 'kingname',  
               'age': 24,  
               'sex': 'male'},  
          'work_info':  
              {'salary': 99999,  
               'position': 'engineer',  
               'department': 'spider'}  
          }  
  
person_json = json.dumps(person_dict, indent=4) # 生成字符串,indent表示缩进情况  
print(person_json)  
print(type(person_json))  
  
data_json = '{"data":{"head":"头", "body":"体"},"status":"true"}'  
data_dict = json.loads(data_json) # loads的s表示的是字符串  
print(data_dict)  
print(type(data_dict))  
print(data_dict['data']['body'])  
  
with open('1.txt') as f:  
    json.load(f) # 这里不带s,传递的是文件的句柄,不常用  
  1. Selenium 安装和使用

1) Selenium 介绍

有时候,网站动态加载的内容经过加密后,我们是无法看懂密文的,但是JavaScript可以看懂,所以如果能一行一行读JavaScript代码,理论上可以读取任何动态加载的页面内容。但问题是大型网站的JavaScript动辄上万行,还经过混淆,肉眼去读几乎不可完成。

大家会发现,在Chrome的“检查”中,Elements选项卡下,我们可以看到被动态加载的内容,说明Chrome检查此时显示的内容,已经是被解析了以后的内容。如果我们能够获得这个被解析的内容,那就能获取到被加密的内容。

这种情况下,就需要使用Selenium来模拟浏览器解析JavaScript,我们再爬取被解析以后的代码。

2) Selenium安装

方式一:

使用pip安装Selenium:

pip install selenium

方式二:

(非必需)下载Selenium的jar包:http://docs.seleniumhq.org/download/

Selenium 的jar包只有在需要使用Selenium的remote WebDriver的时候才有用。这里的remote WebDriver不一定是远程使用,因为有时候如果要处理的网页比较多的话,每次都开一个WebDriver是非常消耗时间的,因此可以在本地或者远程搭建一个WebDriver来节约时间。

方式三:

下载ChromeDriver:https://sites.google.com/a/chromium.org/chromedriver/downloads

下载下来的压缩包解压以后是一个可执行文件。Selenium需要使用的WebDriver才能处理网页,这里的WebDriver我们可以理解为一个小型的浏览器。它可以是Firefox,可以是Chrome也可以是PhantomJS. 其中前两者是有界面的,在处理网页的时候,会弹出一个浏览器窗口。而PhantomJS是没有界面的,这个适合在服务器上来使用。

3) Selenium使用

  1. 初始化webdriver
from selenium import webdriver
driver = webdriver.Chrome('./chromedriver')
driver.get("http://v.youku.com/v_show/id_XMTY2NTk5ODAwMA==.html?from=y1.3-idx-beta-1519-23042.223465.3-3")

这里需要初始化 selenium 的WebDriver, 由于我们使用的是Chrome,所以需要调用:

webdriver.Chrome('ChromeDriver的路径')
其中,ChromeDriver的路径如果和代码在一起,那么就需要写为:./chromedriver

在OS X下的这个ChromeDriver没有后缀名,而如果大家是在Windows下面,ChromeDriver是一个exe的可执行程序,大家就需要把 .exe 加上。另外需要特别注意斜杠的问题,在Windows下面路径中的反斜杠需要使用特殊处理,例如:

webdriver.Chrome(r'D:\test\chromedriver.exe')

注意这里的“r”不能少。

  1. 等待信息出现

由于被动态加载的内容会延迟出现,因此我们需要等待它出现以后才开始抓取。需要使用到WebDriverWait,By 和 expected_conditions

from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as EC

WebDriverWait(driver, 300).until(EC.presence_of_element_located((By.CLASS_NAME, "con")))

WebDriverWait会阻塞程序的运行,并每0.5秒检查一次网页源代码,看我们需要的内容是否已经出现。如果没有出现就继续等待。

在上面的代码中,设定了超时时间为300秒。在300秒内,如果有某个元素出现,那么就解除阻塞,继续运行后面的代码;如果等待的内容始终不出现,那么就会抛出一个超时的Exception。

我们来看一下:

EC.presence_of_element_located((By.CLASS_NAME, "con"))

这里的EC其实就是 expected_conditions,也就是期望的条件。Python接近英语的语法让我们可以非常轻松的看懂这一段代码:

期望的条件.元素出现

而这里的元素就是一个class="con"的元素。

这里除了指定class以外,还可以指定很多其他的属性,例如:

By.ID
By.NAME
By.XPATH

通过元组的形式传递给presence_of_element_located方法。

在网页中寻找我们需要的内容,可以使用类似与Beautiful Soup4 的语法:

element = driver.find_element_by_id("passwd-id") #如果有多个符合条件的,返回第一个
element = driver.find_element_by_name("passwd") #如果有多个符合条件的,返回第一个
element_list = driver.find_elements_by_id("passwd-id") #以列表形式返回所有的符合条件的element
element_list = driver.find_elements_by_name("passwd") #以列表形式返回所有的符合条件的element

也可以使用XPath:

element = driver.find_element_by_xpath("//input[@id='passwd-id']") #如果有多个符合条件的,返回第一个
element = driver.find_element_by_xpath("//input[@id='passwd-id']") #以列表形式返回所有的符合条件的element

但是有一点需要特别注意:这些名字都是find_element开头的,因此他们返回的都是element对象。这些方法他们的目的是寻找element,而不是提取里面的值。

所以当我们使用find_element_by_xpath的时候,不能使用text()这个语句。如果我们想获取里面的文本信息,需要在获取到element以后,再使用element.text。例如:

comment = driver.find_elements_by_xpath('//p[starts-with(@id, "content_")]')
for each in comment:
    print(each.text)

更多Selenium的使用方法,可以参阅它的Python文档:http://selenium-python.readthedocs.io/getting-started.html

模拟登录

一,使用Selenium模拟登录知乎

# -*- coding: utf-8 -*-  
from selenium import webdriver  
from selenium.webdriver.common.keys import Keys  
import time  
  
driver = webdriver.Chrome('./chromedriver.exe') #填写你的chromedriver的路径  
driver.get("https://www.zhihu.com/#signin")  
  
elem = driver.find_element_by_name("account") #寻找账号输入框  
elem.clear()  
elem.send_keys("xxxxxxxxxxx") #输入账号  
password = driver.find_element_by_name('password') #寻找密码输入框  
password.clear()  
password.send_keys("xxxxxxxxxxx") #输入密码  
elem.send_keys(Keys.RETURN) #模拟键盘回车键  
time.sleep(10) #大家这里可以直接sleep, 也可以使用上一课讲到的等待某个条件出现  
print(driver.page_source)  
driver.close() #在视频中,这一行会被注释掉,以方便观察结果  

二、使用cookie登录

# -*- coding: utf-8 -*-  
import requests  
header = {  
            'Content-Type' : 'application/x-www-form-urlencoded; charset=UTF-8',  
            'User-Agent' : 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36',  
            'Referer' : 'https://zhihu.com'  
        }  
session = requests.Session()  
# Request Headers 中的cookie  
cookie = {'Cookie':'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'}  
html = session.get('https://www.zhihu.com/', headers = header, cookies = cookie).content  
print(html) 

三、post 方式登录知乎

# -*- coding: utf-8 -*-  
import requests  
import lxml.html  
  
r = requests.Session()  
zhihuHomeUrl = 'https://www.zhihu.com/#signin'  
userAgent = {'User-Agent':'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36'}  
zhihuHome = r.get(zhihuHomeUrl, headers = userAgent).content  
  
selector = lxml.html.fromstring(zhihuHome)  
xsrf = selector.xpath('/html/body/div[1]/div/div[2]/div[2]/form/input/@value')[0]  
  
data = {'_xsrf':xsrf,'password':'password','captcha_type':'cn','phone_num':'shoujihao'}  
res = r.post('https://www.zhihu.com/login/phone_num', headers=userAgent,data=data) # 登录  
front_page = r.get('https://www.zhihu.com', headers=userAgent) # 登录后就可以访问其他网址了  
print front_page.content  

常见反爬虫

  1. 设置 User-Agent
  2. 设置随机秒数 time.sleep(random.randint(20,50))
  3. 使用代理

突破验证码

人工手动输入

#!/usr/bin/env python  
# -*- coding: utf-8 -*-  
  
import requests  
  
login_url = 'http://my.gfan.com/doLogin'  
email = '352517144@qq.com'  
password = 'LIUhonghe5211'  
  
data = {'gotoUrl': 'http://my.gfan.com/',  
        'loginName': email,  
        'password': password}  
  
header = {'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.116 Safari/537.36',  
          'Accept': 'text / html, application / xhtml + xml, application / xml;q = 0.9, image / webp, * / *;q = 0.8',  
          'Accept-Encoding': 'gzip, deflate, sdch, br',  
          'Accept-Language': 'zh-CN,zh;q=0.8,en;q=0.6'}  
  
session = requests.Session()  
html = session.get(login_url, headers=header).content  
  
captcha_url = 'http://my.gfan.com/captcha'  
cookie = {'Cookie': 'ci_session=a%3A5%3A%7Bs%3A10%3A%22session_id%22%3Bs%3A32%3A%22aa9a878f058dece93c150d3d88a50d9b%22%3Bs%3A10%3A%22ip_address%22%3Bs%3A10%3A%2210.9.19.15%22%3Bs%3A10%3A%22user_agent%22%3Bs%3A108%3A%22Mozilla%2F5.0+%28Windows+NT+6.1%3B+WOW64%29+AppleWebKit%2F537.36+%28KHTML%2C+like+Gecko%29+Chrome%2F56.0.2924.87+Safari%2F537.36%22%3Bs%3A13%3A%22last_activity%22%3Bi%3A1489038787%3Bs%3A9%3A%22user_data%22%3Bs%3A0%3A%22%22%3B%7D6fd9e37ad65db42020a2d9cc02c4f540; PHPSESSID=t8ubq1f350jqqvi8uae6339ql7; myuid=aa180cd4b5448b594ab0649fe739e33e; ngxuid=CgkTD1jA7cNV2D7NAwQNAg=='}  
with open('captcha.png', 'wb') as f:  
    f.write(requests.get(captcha_url,cookies = cookie).content)  
  
captcha_solution = raw_input('captcha code is:')  
data['authCode'] = captcha_solution  
  
result = session.post(login_url, data=data, headers=header).content  
print(result)  

使用cookie登录

图像识别api

  1. 安装 tesseract

(1) Windows

请在这里下载安装包:https://github.com/tesseract-ocr/tesseract/wiki/Downloads

在3rd party Windows exe's/installer下面可以找到exe安装包。

(2) Mac

使用homebrew安装:

brew install tesseract

(3) Linux

使用apt-get安装:

sudo apt-get install tesseract-orc

更多的安装帮助信息,可以参阅:https://github.com/tesseract-ocr/tesseract/wiki

  1. 安装 Python库
pip install Pillow
pip install pytesseract
  1. tesseract的使用

tesseract的使用非常简单,流程如下:

  • 导入相关的第三方库
  • 打开图片
  • 识别

我们可以通过以下代码来实现最简单的图片识别:

import pytesseract
from PIL import Image
image = Image.open('验证码.png')
code = pytesseract.image_to_string(image)
print(code)

如果你的系统为Mac OS,并且使用homebrew安装tesseract,那么你一定会遇到下面的报错信息:

你需要修改Python安装文件夹下面的lib/site-packages/pytesseract文件夹下面的 pytesseract.py 文件,将第60行的:

tesseract_cmd = 'tesseract'

修改为:

tesseract_cmd = '/usr/local/bin/tesseract'
  1. 打码网站的使用

GfanWeb.py

import requests
import time
import json


login_url = 'http://my.gfan.com/doLogin'
email = 'greensouth@foxmail.com'
password = 'jikexueyuan'

captcha_username = 'kingname'
captcha_password = '84710086'
captcha_appid = 1
captcha_appkey = '22cc5376925e9387a23cf797cb9ba745'
captcha_codetype = '1004'
captcha_url = 'http://api.yundama.com/api.php?method=upload'
captcha_result_url = 'http://api.yundama.com/api.php?cid={}&method=result'

data_gfan = {'gotoUrl': 'http://my.gfan.com/',
             'loginName': email,
             'password': password}
gfan_captcha_url = 'http://my.gfan.com/captcha'
header = {'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.116 Safari/537.36'}


def get_captcha_by_cid(cid, timeout=15):
    """

    :param cid: int
    :param timeout: int
    :return: captcha

    在不能立刻获得验证码的情况下,反复查询直到获取为止或者超时为止。
    """
    while timeout > 0:
        response = requests.get(captcha_result_url.format(cid)).text
        response_dict = json.loads(response)
        print(response_dict, '——还剩:{}秒...'.format(timeout))
        captcha = response_dict['text']
        if response_dict['text']:
            return captcha
        time.sleep(1)
        timeout -= 1
    return ''


def query_captcha(filename):
    """

    :param filename: string
    :return: string

    上传验证码到打码网站,并获取cid.
    """
    data = {'method': 'upload', 'username': captcha_username, 'password': captcha_password, 'appid': captcha_appid,
            'appkey': captcha_appkey, 'codetype': captcha_codetype, 'timeout': '60'}
    f = open(filename, 'rb')
    file = {'file': f}
    response = requests.post(captcha_url, data, files=file).text
    f.close()
    response_dict = json.loads(response)
    print(response_dict)
    captcha = response_dict['text']
    if not captcha:
        cid = response_dict['cid']
        captcha = get_captcha_by_cid(cid)

    return captcha


session = requests.Session()

with open('captcha2.png', 'wb') as f:
    f.write(requests.get(gfan_captcha_url).content)

captcha_solution = query_captcha('captcha2.png')
data_gfan['authCode'] = captcha_solution
result = session.post(login_url, data=data_gfan, headers=header).content
print(result.decode())
» 转载请注明来源:呢喃 » 爬虫系列学习笔记(一)