单线程爬虫

一、Requests介绍与安装

1 Requests介绍:HTTP for humans

是Python的第三方HTTP库,可方便地实现Python的网络连接;

完美替代了Python的urllib2模块;

拥有更多自动化、更友好的用户体验、更完善的功能;


2 安装

在Windows下,执行pip install requests;

在Linux下,执行sudo pip install requests;


3 第三方库安装技巧

3.1 少使用easy_install,因为只能安装不能卸载;

3.2 多使用pip方式安装;

3.3 撞墙怎么办?请戳http://www.lfd.uci.edu/~gohlke/pyhtonlibs/


4 举例

教学视频是根据上述网站下载requests库,但现在没法打开,因此在搜索引擎上搜索查找到requests下载链接:https://pypi.python.org/pypi/requests/

选择“requests-2.10.0-py2.py3-none-any.whl (md5)”进行下载;

修改下载文件的拓展名为.zip,并复制requests文件夹到Python安装目录下的Lib文件夹下即完成安装;




二、第一个网页爬虫

使用requests获取网页源代码,再使用正则表达式匹配出感兴趣的内容,这是单线程简单爬虫的基本原理;

1 使用requests获取网页源代码

1.1 直接获取源代码

#-*-coding:utf8-*-
import requests

html=requests.get('http://www.nowcoder.com/courses')

print html.text

但有时通过上述Python程序是无法获取网页源代码,因为有的网页会有反爬虫机制;


1.2 修改HTTP头获取源代码;

import re
import requests

# 相当于面具,让访问的网站误以为是浏览器再查看源代码
# 获取User-Agent内容:在该网页右击选择审核元素,切换到Network栏,刷新页面,随便点击一项,在Headers一栏最下方就有相关信息
# hea是字典,键是User-Agent,值就是后面一长串内容
hea={'User-Agent':'Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.152 Safari/537.36'}

# 在此处调用hea
html=requests.get('http://jp.tingroom.com/yuedu/yd300p/',headers=hea)

# 将编码转为utf-8,否则中文会显示乱码
html.encoding='utf-8'

print html.text


2 requests配合正则表达式提取感兴趣的内容:打印该网页中的日文

先获取整个网页源代码;

再查看想要获取的内容的上下文有什么共同点,可以看到想要获取的内容都是以<span style="color:#666666;">开头,以</span></p></li>结尾,中间部分就是带获取内容(.*?);

import re
import requests

hea={'User-Agent':'Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.152 Safari/537.36'}

html=requests.get('http://jp.tingroom.com/yuedu/yd300p/',headers=hea)

html.encoding='utf-8'

# print html.text

course=re.findall('<span style="color:#666666;">(.*?)</span></p></li>',html.text,re.S)

for i in course:
    print i

执行结果:

第二章 昔々、といってもせいぜい二十年ぐらい前のことなのだ...
挪威的森林(中日对照) 内容简介: 汉堡机场一曲忧郁的《挪...
藤野先生名文选读中日文对照 東京も格別のことはなかつた。上...
夏目漱石 我是猫(中日对照) 吾輩は猫である 夏目漱石 一 吾輩...
それお皿の絵柄よ 足のケガで中クラスの総合病院に入院しまし...
あるけちん坊(ぼう)な男がおりました。 毎日毎日,ご飯どき...
あるところに,たいへんへそまがりな息子(むすこ)がおりま...
向こうから,お医者(いしゃ)がやってきました。そこへ店(...
お盆休み(ぼんやすみ)に帰ってきた者(もの)同士(どうし...
昔(むかし),三太(さんた)という,ばかな息子がおりまし...
ある日のこと。 そこつ者が,瀬戸物(せともの)屋(や)へ,...
植木(うえき)の大好きな旦那(だんな)がおりました。 ある...
息子が表(おもて)で,凧(たこ)を揚(あ)げておりました...
ある夜(や)のこと,お寺(てら)の庭(にわ)で,小僧(こ...
はくうんしゅうしょく 一匹のトンボが夏の終わりを告げるわけ...
先日、ある研修で聞いた言葉ですが、 「学習」の本質を端的に...
闇夜(やみよ)に,二人の若い男が,こそこそ話しております...
ある役人が誕生日のときに、下役たちは彼が鼠年だと聞き、お...
東京の郊外に住む木村さんは、お酒を飲んでの失敗の多い人で...
ある人が新調した絹の裾/裙(すそ、はかま)を着用して外出...




三、向网页提交数据

1 Get与Post介绍

Get从服务器上获取数据,通过构造url中的参数实现功能;

Post向服务器传送数据,将数据放在header提交数据;


2 分析目标网站

目标网站https://www.crowdfunder.com/browse/deals

爬取内容:网页的所有公司名称

辅助工具:Chrome浏览器的审核元素中Network功能

核心方法:requests.post

核心步骤:构造表单 -> 提交表单 -> 获取返回信息


1‘ 打开网站,点击查看网页源代码,这是网页爬虫的必要习惯!

在源代码中,可先用公司名称搜索其在源代码中的位置,如公司名称MYFORCE在源代码位置如下:


公司名称可使用正则表达式"card-title">(.*?)</div>进行匹配,此时可匹配到19个公司名称;


2‘ 在网站正下方点击SHOW MORE按钮,出现更多公司信息(以追加形式);

值得注意的是,此时网站网址仍是https://www.crowdfunder.com/browse/deals,说明该网站使用异步加载技巧

异步加载,指先加载网页基本框架,对于剩下的信息,需要哪里就加载哪里,提高网页加载效率

重新打开网站源代码,搜索"card-title">,发现仍然是19,说明后面加载的公司信息在源代码中无法找到;


3使用Chrome的审核元素中Network功能,当前没有任何信息;点击SHOW MORE按钮,出现信息,点击第一条:

在General项,请求方法是Request Method: POST,说明使用POST向网页提交数据;

提交地址是https://www.crowdfunder.com/browse/deals&template=false


在Form Data项,页数是Page: 2,说明构造Form Data向网页进行提交,可以得到想要的信息;



4‘ 打开Pycharm,

#-*-coding:utf8-*-
import requests
import re
url='https://www.crowdfunder.com/browse/deals&template=false'
data={   # 字典,有2个元素,构造Form Data
	'entities_only':'true',
	'page':'1'   # 可修改页数值
}
html_post=requests.post(url,data=data)
title=re.findall('"card-title">(.*?)</div>',html_post.text,re.S)
for each in title:
	print each

结果执行:

MYCROWD QA
DIGITZS
GOCOIN
SANTO DIABLO MEZCAL
RAGEON!
SELFIE WITH ME




四、实战——极客学院课程爬虫

1 目标网站

http://www.jikexueyuan.com/course/


2 目标内容

前20页的课程名称、课程介绍、课程时间、课程等级、学习人数


3 涉及知识

requests获取网页、re.sub换页、正则表达式匹配内容


4 抓取关键步骤

1’ 打开网站,首页网站为http://www.jikexueyuan.com/course/;点击下一页后,第2页网站为http://www.jikexueyuan.com/course/?pageNum=2;

则首页网站也可以是http://www.jikexueyuan.com/course/?pageNum=1;


2‘ 首先点击审核元素中放大镜图标,将鼠标箭头放在第一个课程的名称处,审核元素就会自动定位到当前源代码相应位置,在其附近也可找到课程介绍、时间、等级和学习人数对应的源代码位置;


3’ 采用先抓大后抓小的原则。对于先抓大,在审核元素中,每个课程信息都是在标签<li id="和标签</li>之间,可以使用re.findall()匹配出所有课程信息并保存在列表中;对于再抓小,对列表进行遍历,每个课程信息进行细划分,如下:

课程名称在标签class="lessonimg" title="和标签" alt=之间;

课程介绍在标签display: none;">和标签</p>之间;

课程时间在标签<em>和标签</em>之间;

课程等级在标签<em>和标签</em>之间;

学习人数在标签"learn-number">和标签</em>之间;

其中,对于相同的标签标识,可使用re.findall()匹配出2个符合要求的元素,课程时间在列表的第一个元素,课程等级在列表的第二个元素;


5 编写程序——从小到大功能实现

5.1 实现单页的课程信息爬取

#-*-coding:utf8-*-
import requests
import re

import sys
reload(sys)
sys.setdefaultencoding("utf-8")

url='http://www.jikexueyuan.com/course/?pageNum=1'

html=requests.get(url).text
course=re.findall('(<li id=".*?</li>)',html,re.S)
info={}

for each in course:
    info['title']=re.search('class="lessonimg" title="(.*?)" alt=',each,re.S).group(1)
    info['detail'] = re.search('display: none;">(.*?)</p>', each, re.S).group(1)
    temp=re.findall('<em>(.*?)</em>',each,re.S)
    info['date']=temp[0]
    info['class']=temp[1]
    info['number']=re.search('"learn-number">(.*?)</em>',each,re.S).group(1)

    f = open('info.txt', 'a')
    f.writelines('title: '+info['title']+'\n')
    f.writelines('detail: ' + info['detail'] + '\n')
    f.writelines('date: ' + info['date'] + '\n')
    f.writelines('class: ' + info['class'] + '\n')
    f.writelines('number: ' + info['number'] + '\n\n')
    f.close()


5.2 实现20页的课程信息爬取

#-*-coding:utf8-*-
import requests
import re

import sys
reload(sys)
sys.setdefaultencoding("utf-8")

url='http://www.jikexueyuan.com/course/?pageNum=1'

cur_url=int(re.search('pageNum=(\d+)',url,re.S).group(1))
page_group=[]
for i in range(1,21):
    links=re.sub('pageNum=\d+','pageNum=%s'%i,url,re.S)
    page_group.append(links)



for link in page_group:
    print u'正在处理页面:'+link
    html=requests.get(link).text
    course=re.findall('(<li id=".*?</li>)',html,re.S)
    info={}

    for each in course:
        info['title']=re.search('class="lessonimg" title="(.*?) alt=',each,re.S).group(1)
        info['detail'] = re.search('display: none;">(.*?)</p>', each, re.S).group(1)
        temp=re.findall('<em>(.*?)</em>',each,re.S)
        info['date']=temp[0]
        info['class']=temp[1]
        info['number']=re.search('"learn-number">(.*?)</em>',each,re.S).group(1)

        f = open('info.txt', 'a')
        f.writelines('title: '+info['title']+'\n')
        f.writelines('detail: ' + info['detail'] + '\n')
        f.writelines('date: ' + info['date'] + '\n')
        f.writelines('class: ' + info['class'] + '\n')
        f.writelines('number: ' + info['number'] + '\n\n')
        f.close()


5.3 实现使用类的课程信息爬取

#-*_coding:utf8-*-
import requests
import re

# Windows下默认编码是GBK,网页默认编码是utf-8,编码不匹配易导致爬取内容乱码
import sys
reload(sys)
sys.setdefaultencoding("utf-8")

class spider(object):
    def __init__(self):
        print u'开始爬取内容。。。'

#getsource用来获取网页源代码
    def getsource(self,url):
        html = requests.get(url)
        return html.text

#changepage用来生产不同页数的链接
    def changepage(self,url,total_page):
        now_page = int(re.search('pageNum=(\d+)',url,re.S).group(1))
        page_group = []
        for i in range(now_page,total_page+1): # 左闭右开
            link = re.sub('pageNum=\d+','pageNum=%s'%i,url,re.S)
            page_group.append(link)
        return page_group

#geteveryclass用来抓取每个课程块的信息
    def geteveryclass(self,source):
        everyclass = re.findall('(<li id=.*?</li>)',source,re.S)
        return everyclass

#getinfo用来从每个课程块中提取出我们需要的信息
    def getinfo(self,eachclass):
        info = {} # 字典
        info['title'] = re.search('lessonimg" title="(.*?)" alt',eachclass,re.S).group(1)
        info['content'] = re.search('display: none;">(.*?)</p>',eachclass,re.S).group(1)
        timeandlevel = re.findall('<em>(.*?)</em>',eachclass,re.S)
        info['classtime'] = timeandlevel[0]
        info['classlevel'] = timeandlevel[1]
        info['learnnum'] = re.search('"learn-number">(.*?)</em>',eachclass,re.S).group(1)
        return info

#saveinfo用来保存结果到info.txt文件中
    def saveinfo(self,classinfo):
        f = open('info.txt','a')
        #追加方式打开
        for each in classinfo:
            f.writelines('title:' + each['title'] + '\n')
            f.writelines('content:' + each['content'] + '\n')
            f.writelines('classtime:' + each['classtime'] + '\n')
            f.writelines('classlevel:' + each['classlevel'] + '\n')
            f.writelines('learnnum:' + each['learnnum'] +'\n\n')
        f.close()

#####程序入口
#如果是程序自己使用jikexueyuan.py,则__name__的值是__main__
if __name__ == '__main__':

    classinfo = [] # 空列表,用以保存课程信息
    url = 'http://www.jikexueyuan.com/course/?pageNum=1'
    jikespider = spider()    # 实例化spider()类
    all_links = jikespider.changepage(url,20)
    for link in all_links:
        print u'正在处理页面:' + link
        html = jikespider.getsource(link)
        everyclass = jikespider.geteveryclass(html)
        for each in everyclass:
            info = jikespider.getinfo(each)
            classinfo.append(info)
    jikespider.saveinfo(classinfo)


6 涉及知识点总结

6.1 修改默认编码

Windows默认编码是GBK,网页默认编码是UTF-8,需设置默认编码,否则易导致爬取内容乱码;

注意:在设置前必须对sys模块进行reload;

import sys
reload(sys)
sys.setdefaultencoding("utf-8")

6.2 __name__内置属性

如果import一个模块,则模块的__name__的值为模块文件名,不带路径或文件扩展名;

如果直接执行当前文件,则__name__的值为__main__。

6.3 类的定义和实例化

6.4 re.search(pattern, string, flag)方法

pattern为匹配的正则表达式,string为要匹配的字符串,flag为标志位;

匹配整个字符,只提取第一个符合要求的内容,返回一个正则表达式对象;

使用匹配对象函数group(n)获取第n个匹配字符串,或group()获取整个字符串;

6.5 re.findall(pattern, string, flag)方法

匹配所有符合要求的内容,结果以列表形式返回;

可使用下标对列表元素进行访问;

6.6 re.sub(pattern, repl, string)方法

pattern为匹配的正则表达式,repl为替换的正则表达式,string为要匹配的字符串;

如re.sub('123(.*?)','123%d'%123,s)表示将123后面的字符串替换成123,这里的%d可以是任何一个字母;

6.7 正则表达式\d+

\d表示[0-9]

\l表示[a-z]

\u表示[A-Z]

\w表示[a-zA-Z0-9]

\s表示任意空白字符

上述大写形式表示其相反意义,如\D表示非数字字符

+表示有多个,如\d+表示至少有1个、最多不限制的数字串

^表示以其后紧跟的字符类型开头,如^[0-9]表示以数字字符为开头,注意:[^0-9]表示非数字字符

$表示以其前紧跟的字符类型结尾

6.8 文件I/O

用file对象实现文件操作;

a 打开:open()方法

file_object=open(file_name[, access_mode]);

file_name表示要访问的文件名的字符串值,access_mode表示打开文件模式:只读r、写入w、追加a等;

b 关闭:close()方法

刷新缓存区重任何还没写入的信息,并关闭该文件;

file_object.close();

c 写:

write()方法:

file_object.write(string);

将任何字符串写入一个打开的文件;

writelines()方法:

file_object.writelines(sequence);

将序列以迭代形式写入一个打开的文件;

d 读:

read()方法:

string=file_object.read([len]);

从打开的文件中读取一个字符串,默认读到文件末尾,否则读取前len个字符,以字符串形式返回;

readline()方法:

string=file_object.readline();

从打开的文件中读取整行,包括换行符,以字符串形式返回;

sequence=file_object.readlines();

从打开的文件中读取所有行,以列表形式返回;

---------------------------------------------------------------

课程总结:

1 requests获取网页源代码

2 修改http头绕过简单的反爬虫机制

3 向网页提交内容


  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值