您的位置:首页 > 编程语言 > Python开发

【极客学院】-python学习笔记-4-单线程爬虫 (提交表单抓取信息,实战练习)

2016-01-12 15:55 986 查看
极客学院课程地址:http://www.jikexueyuan.com/course/821_3.html?ss=1

向网页提交数据:

get,post 交互方式

分析目标网站

表单提交功能

get:从服务器获得数据

post:向服务器传数据

eg:
https://www.crowdfunder.com/browse/deals
request.post

步骤:构造表单,提交,获取返回信息

异步加载:菜做好一道上一道

不同于 菜做好了,再一次性上齐

网页 → 审查 → Headers → Request Method:GET (页面并没有 more,这里也没有POST)

Form Data → page:2

当有多个 page 的时候,直接提取,可能因为异步加载,无法提取到 more 的信息

这时 可以建立一个字典,并用 request.post 去提交表单,提取数据

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

url='https://www.crowdfunder.com/'

data = {
'entities_only':'true'<span style="background-color: rgb(153, 255, 153);">,</span>
'page':'1'
}

html_post = requests.post(url<span style="background-color: rgb(153, 255, 153);">,</span>data=data)
title = re.findall('"card-title">(.*?)</div>', html_post.text, re.S)

for each in title:
print (each)


继续

实战:
http://www.jikexueyuan.com/course/
前20页的 课程名称,课程介绍,课程时间,课程等级,学习人数

步骤:

requests获取网页

re.sub换页

正则表达式匹配内容

问题:

课程介绍,后面是省略号 的情况该怎么办?

爬虫只能爬到可以看到的

有的课程的课程介绍也可能为空

解决方案:

先抓大后抓小:

另:windows下将命令提示符的编码强制转化成utf-8

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


实践中遇到问题:

按课程中的代码敲出来后,会有一些错误

比如运行后,info.txt 中没有内容:

因为:网页的元素不是课程里的那样了,所以需要修改匹配的线索,这样修改了 everyclass,title 和 content,



<li id="2430" test="0" deg="0" >

但是到 timeandlevel 又有错误了

TypeError: '_sre.SRE_Match' object is not subscriptable


说明:

“XXX”object is not subscriptable

说的是XXX对象不是可索引的,可索引的对象有list,tuple等

如果你确定你的XXX是一个可迭代对象的话,可以尝试用list()函数把它转化为列表,然后通过索引读取元素

尝试分部找规律,提出 time 和 level,

info['classtime'] = re.search('"time-icon"></i><em>(.*?)</em>',eachclass,re.S).group(1)
info['classlevel'] = re.search('"xinhao-<span style="color:#ff0000;">icon</span>"></i><em>(.*?)</em>',eachclass,re.S).group(1)


但是 有的 level 的 icon 后面可能会加数字,导致,查找出的结果会有none

<i class="xinhao-icon2"></i><em>中级</em>

后来发现问题在于,把 re.findall 写成了 re.search

timeandlevel = re.search('<em>(.*?)</em>',eachclass,re.S)


所以,只查找了一个 em,没办法分配哦

<_sre.SRE_Match object; span=(1122, 1146), match='<em>3课时\n\t\t\t\t\t\t\t57分钟</em>'>


而实际上是这样的数据

<dd class="mar-b8"><i class="time-icon"></i><em>4课时
<span style="white-space:pre">	</span>56分钟</em>
</dd>
<dd class="zhongji">

<span style="white-space:pre">	</span><i class="xinhao-icon"></i><em>初级</em>
</dd>


最终代码:

#-*-coding:utf8-*-

import requests
import re

#url = 'http://www.jikexueyuan.com/course/'

#html = requests.get(url)

#print(html.text)

#content = re.findall('', html.text, re.S)

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

def changepage(self,url,total_page):

page_group = []                 #获得需要爬取的所有 page 的列表

now_page = int(re.search('pageNum=(\d+)', url, re.S).group(1))

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

def getsource(self,url):

html = requests.get(url)        #获取网页源代码

return html.text

def geteveryclass(self,source):

#everyclass = re.findall('(<li deg="".*?</li>)',source,re.S )        #先把所有课程找出来
everyclass = re.findall('(<li id=.*?</li>)',source,re.S )

return everyclass

def getinfo(self,eachclass):

info = {}                       #定义字典

# info['title'] = re.search('target="_blank">(.*?)</a>',eachclass,re.S).group(1)
info['title'] = re.search('title="(.*?)" alt',eachclass,re.S).group(1)
# info['content'] = re.search('</h2><p>(.*?)</p>',eachclass,re.S).group(1)
info['content'] = re.search('none;">(.*?)</p>',eachclass,re.S).group(1)
#timeandlevel = re.search('<em>(.*?)</em>',eachclass,re.S)
timeandlevel = re.<span style="background-color: rgb(153, 255, 153);">findall</span>('<em>(.*?)</em>',eachclass,re.S)
info['classtime'] = timeandlevel[0]
info['classlevel'] = timeandlevel[1]
# info['classtime'] = re.search('"time-icon"></i><em>(.*?)</em>',eachclass,re.S).group(1)
# info['classlevel'] = re.search('"xinhao-icon"></i><em>(.*?)</em>',eachclass,re.S).group(1)
info['learnnum'] = re.search('"learn-number">(.*?)</em>',eachclass,re.S).group(1)

return info

def saveinfo(self,classinfo):

f = open('info.txt','a')            #打开 info.txt, 使用追加的方式

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()

#程序入口

if __name__ == '__main__':          #如果程序是自己使用 .py 文件运行

classinfo = []                  #定义一个列表用来存课程信息

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

jikespider = spider()           #实例化 类
all_links = jikespider.changepage(url,2)

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)


知识点:

参考:

Python中re(正则表达式)模块学习 http://www.cnblogs.com/sevenyuan/archive/2010/12/06/1898075.html
[1]re.search
  re.search函数会在字符串内查找模式匹配,只到找到第一个匹配然后返回,如果字符串没有匹配,则返回None。
re.search的函数原型为: re.search(pattern, string, flags)
每个参数的含意与re.match一样。
re.match与re.search的区别:
re.match只匹配字符串的开始,如果字符串开始不符合正则表达式,则匹配失败,函数返回None;而re.search匹配整个字符串,直到找到一个匹配。
now_page = int(re.search('pageNum=(\d+)', url, re.S).group(1))


python group()

参考:http://www.cnblogs.com/kaituorensheng/archive/2012/08/20/2648209.html

正则表达式中,group()用来提出分组截获的字符串,()用来分组

例:

import re
a = "123abc456"
print re.search("([0-9]*)([a-z]*)([0-9]*)",a).group(0)   #123abc456,返回整体
print re.search("([0-9]*)([a-z]*)([0-9]*)",a).group(1)   #123
print re.search("([0-9]*)([a-z]*)([0-9]*)",a).group(2)   #abc
print re.search("([0-9]*)([a-z]*)([0-9]*)",a).group(3)   #456


re.search('pageNum=(\d+)', url, re.S)    <span style="white-space:pre">		</span>#<_sre.SRE_Match object; span=(35, 44), match='pageNum=1'>
re.search('pageNum=(\d+)', url, re.S).group(1)<span style="white-space:pre">		</span># 1


re.match
  re.match 尝试从字符串的开始匹配一个模式,如:下面的例子匹配第一个单词。
re.match的函数原型为:re.match(pattern, string, flags)
第一个参数是正则表达式,这里为"(\w+)\s",如果匹配成功,则返回一个Match,否则返回一个None;
第二个参数表示要匹配的字符串;
第三个参数是标致位,用于控制正则表达式的匹配方式,如:是否区分大小写,多行匹配等等。

[2]re.sub
  re.sub用于替换字符串中的匹配项。下面一个例子将字符串中的空格 ' ' 替换成 '-' :

re.sub的函数原型为:re.sub(pattern, repl, string, count)
其中第二个参数是替换后的字符串;本例中为'-'
第四个参数指替换个数。默认为0,表示每个匹配项都替换。
re.sub还允许使用函数对匹配项的替换进行复杂的处理。如:re.sub(r'\s', lambda m: '[' + m.group(0) + ']', text, 0);将字符串中的空格' '替换为'[ ]'。
for i in range(1,3):
link = re.sub('pageNum=\d+','pageNum=%s'%i,url, re.S)
print(link)
结果:
http://www.jikexueyuan.com/course/?pageNum=1 http://www.jikexueyuan.com/course/?pageNum=2[/code] 
[3]re.findall
  re.findall可以获取字符串中所有匹配的字符串。如:re.findall(r'\w*oo\w*', text);获取字符串中,包含'oo'的所有单词。

everyclass = re.findall('(<li id=.*?</li>)',source,re.S)


timeandlevel = re.findall('<em>(.*?)</em>',eachclass,re.S)
info['classtime'] = timeandlevel[0]
info['classlevel'] = timeandlevel[1]
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: