Crossin的编程教室

标题: Python 实战(5):拿来主义 [打印本页]

作者: crossin先生    时间: 2015-5-17 23:32
标题: Python 实战(5):拿来主义
someweb-05.png
有了列表,有了详细信息,有了搜索,这个电影网站已经有了基本的结构。现在要做的是:获取更多的内容。

我们没有必要也不可能自己去生产数量庞大的电影信息,互联网上的资源已足够满足我们的需求。(不过如果你要使用这些资源进行商业用途,请尊重内容来源方的版权。)

这个项目里,我将用豆瓣电影的 API 来获取内容。不要问我如何知道豆瓣有 API 可以做这样的事。我只是觉得它应该有,然后就去搜索引擎里搜索“豆瓣 api”,结果真的有。大概扫了下文档和示例,发现还挺好用的,于是就它了。

类似的情况还有之前的“查天气”系列课程(微信回复 43 可见),有人问是怎么知道获取天气的 API 地址,另外最初的地址现在已失效,如何更换新地址。对于这些,我也并不比各位了解更多,只是在需要的时候去网上搜索,发掘想要的答案。作为一名程序员,正确使用搜索引擎是最基本的技能。

现在许多内容型网站都将其数据开放了 API 供开发者使用,包括天气预报、电影、图书、地图、商户信息等等。对于没有 API 的网站,也可以通过直接抓取网页上的内容获得数据,也就是通常说的“爬虫”。API 和爬虫的区别在于,API 是内容提供方将信息整理好主动提供给你,数据有标准的格式,但使用时会受一定的限制;爬虫则是你直接从网页上的展现内容里去分析并提取你要的信息,一般来说是未经授权的。从实现上来说,API 会比爬虫简单许多,只要按照接口规范就很容易获取数据。

豆瓣 API 有比较详细的文档,各位可自行查阅。在文档中,我看到了两个比较合适的接口:

Top250(/v2/movie/top250),获取豆瓣电影排行榜前 250 部电影列表;
电影条目信息(/v2/movie/subject/:id),获取一部电影的详细信息。

所以我打算在抓取信息代码中,首先通过 Top250 接口获取 250 部电影的 id,之后再根据 id 查询每一部的详细信息。

这个代码并不作为网站功能的一部,而是直接通过命令行运行。如果你想在网页上实现此功能,会有一个问题,就是抓取过程是个很耗时的事情,但一个网页请求并不能等待很久,如果一段时间未返回,这个请求就会关闭。暂时我们还不需要去解决这个问题。

首先获取 Top250。使用最基本的 urllib 请求 API:

import urllib
response = urllib.urlopen('http://api.douban.com/v2/movie/top250')
data = response.read()
print data

可以看到结果是一长串 json 格式的文本,这就是我们想要的结果。建议在浏览器中打开此 API 地址,并且用 json 插件或工具查看返回信息,这样可以更直观地看到数据的结构。

someweb-05-1.png

将 json 格式转换成 dict 对象:

import json
data_json = json.loads(data)

dict 类型的结果中,subjects 对应的是影片 list:

movie250 = data_json['subjects']
for movie in movie250:
    print movie['id'], movie['title']

打印出结果,发现只有 20 条。又查了下文档,原来 Top250 接口还有两个可选参数:start 和 count,表示从第几位开始取,取多少条。

那么把代码调整下,外面增加一层循环。为了避免连续请求太快,在每次循环中,通过  time.sleep 方法停顿 3 秒钟。另外,把取到的电影 id 都存入一个变量中:

import urllib
import json
import time

movie_ids = []
for index in range(0, 250 ,50):
    print index
    response = urllib.urlopen('http://api.douban.com/v2/movie/top250?start=%d&count=50' % index)
    data = response.read()
    # print data

    data_json = json.loads(data)
    movie250 = data_json['subjects']
    for movie in movie250:
        movie_ids.append(movie['id'])
        print movie['id'], movie['title']
    time.sleep(3)
print movie_ids

一切顺利,拿到 250 个 id,接下来就可以进行第二步,获取影片的详细信息了。这里,你可以把打印出的 movie_ids 保存下来,避免后续过程中的重复抓取。

someweb-05-2.png
为了能把抓取到的数据保存下来,先对我们之前的数据库做一些改动。结合文档中对条目信息的说明,决定在数据库中记录以下字段:

id - 影片 id
title - 中文名
origin - 原名
url - 影片豆瓣链接
rating - 评分
image - 海报图片地址
directors - 导演
casts - 主演
year - 年代
genres - 类型
countries - 制片国家/地区
summary - 简介

在数据库中执行:

create table movie (id, title, origin, url, rating, image, directors, casts, year, genres, countries, summary);

添加数据的 sql 语句不需要了,我们将通过抓取程序来添加。

直接复用 web.py 里的数据库方法:

import web
db = web.database(dbn='sqlite', db='MovieSite.db')

观察一下条目信息 API 的格式,增加一个将此格式数据存入数据库的方法:

def add_movie(data):
    movie = json.loads(data)
    print movie['title']
    db.insert('movie',
        id=int(movie['id']),
        title=movie['title'],
        origin=movie['original_title'],
        url=movie['alt'],
        rating=movie['rating']['average'],
        image=movie['images']['large'],
        directors=','.join([d['name'] for d in movie['directors']]),
        casts=','.join([c['name'] for c in movie['casts']]),
        year=movie['year'],
        genres=','.join(movie['genres']),
        countries=','.join(movie['countries']),
        summary=movie['summary'],
    )

之后,就是开始让程序反复地去请求、转换、存储。同样,用 sleep 保持节奏,另外 print 出一些信息,以便于了解抓取的进度。这么做也是为了在程序意外中断后,可以手动从中断处开始继续抓取。

count = 0
for mid in movie_ids:
    print count, mid
    response = urllib.urlopen('http://api.douban.com/v2/movie/subject/%s' % mid)
    data = response.read()
    add_movie(data)
    count += 1
    time.sleep(3)

一切就绪,奔跑吧,程序!向着 250 部电影数据。

现在再运行网站,数据已经比较丰富了。不过因为数据库表名的变动,详细数据页会有错误。这个小修改就留给你们自己了。

get_movie.py (4.28 KB, 下载次数: 339)




作者: Tig    时间: 2015-5-18 20:33
编码问题真是太烦了
请教一下先生
系统问题,python2.7不能导入web.py 但是3可以。就用3写
python3提示json的类型不能是byte..我就encoding成utf-8,但是最终插入数据库的时候又成了乱码有没有什么解决办法
把插入的数据也编码成utf-8,但是没有用。
作者: 飞花拿么朵    时间: 2015-5-19 16:31
Tig 发表于 2015-5-18 20:33
编码问题真是太烦了
请教一下先生
系统问题,python2.7不能导入web.py 但是3可以。就用3写

楼上什么系统,我ubuntu,win7都试了没问题啊
作者: Tig    时间: 2015-5-19 16:41
飞花拿么朵 发表于 2015-5-19 16:31
楼上什么系统,我ubuntu,win7都试了没问题啊

win7 64的
linux下都没问题,就是WIN7下不能。。
作者: 飞花拿么朵    时间: 2015-5-19 16:54
哦,我的win7是32位的,可能是64的问题
作者: Tig    时间: 2015-5-19 20:53
终于弄好了0 0。。搓手等更新呢
作者: csyhhb    时间: 2015-5-26 17:45
Traceback (most recent call last):
  File "E:\Program Files\Python279\lib\site-packages\web\wsgiserver\__init__.py", line 1245, in communicate
    req.respond()
  File "E:\Program Files\Python279\lib\site-packages\web\wsgiserver\__init__.py", line 775, in respond
    self.server.gateway(self).respond()
  File "E:\Program Files\Python279\lib\site-packages\web\wsgiserver\__init__.py", line 2018, in respond
    response = self.req.server.wsgi_app(self.env, self.start_response)
  File "E:\Program Files\Python279\lib\site-packages\web\httpserver.py", line 306, in __call__
    return self.app(environ, xstart_response)
  File "E:\Program Files\Python279\lib\site-packages\web\httpserver.py", line 274, in __call__
    return self.app(environ, start_response)
  File "E:\Program Files\Python279\lib\site-packages\web\application.py", line 279, in wsgi
    result = self.handle_with_processors()
  File "E:\Program Files\Python279\lib\site-packages\web\application.py", line 249, in handle_with_processors
    return process(self.processors)
  File "E:\Program Files\Python279\lib\site-packages\web\application.py", line 245, in process
    print >> web.debug, traceback.format_exc()
  File "E:\Program Files\Python279\lib\site-packages\web\webapi.py", line 478, in _debugwrite
    out.write(x)
IOError: [Errno 22] Invalid argument
我这个错是什么原因?
作者: crossin先生    时间: 2015-5-26 20:13
csyhhb 发表于 2015-5-26 17:45
Traceback (most recent call last):
  File "E:\Program Files\Python279\lib\site-packages\web\wsgiserv ...

不知道,你运行了啥,完全没看出原因……
作者: pandaqr    时间: 2015-6-1 20:26
本帖最后由 pandaqr 于 2015-6-1 20:27 编辑

老师好,不知道为什么,最后我的页面上最后只显示了26部电影,会是哪一步出错了么?

屏幕快照 2015-06-01 下午8.14.30.png (83.04 KB, 下载次数: 469)

屏幕快照 2015-06-01 下午8.14.30.png


作者: crossin先生    时间: 2015-6-2 23:11
pandaqr 发表于 2015-6-1 20:26
老师好,不知道为什么,最后我的页面上最后只显示了26部电影,会是哪一步出错了么?  ...

是不是数据库没抓全?你去sqlite里面看看数据
作者: lovepythoner    时间: 2015-7-6 11:41
加载了两遍后提示
{u'msg': u'You API access rate limit has been exceeded.  Contact api-master@douban.com if you want higher limit. ', u'code': 1998, u'request': u'GET /v2/movie/top250'}
豆瓣太小气了
作者: crossin先生    时间: 2015-7-6 16:05
lovepythoner 发表于 2015-7-6 11:41
加载了两遍后提示
{u'msg': u'You API access rate limit has been exceeded.  Contact  if you want highe ...

果然又印证那个定律:我在课里说了什么api,很快这个api就不能用了
作者: lincoln    时间: 2015-7-12 14:20
打印出结果,发现只有 20 条。又查了下文档,原来 Top250 接口还有两个可选参数:start 和 count,表示从第几位开始取,取多少条。

那么把代码调整下,外面增加一层循环。为了避免连续请求太快,在每次循环中,通过  time.sleep 方法停顿 3 秒钟。另外,把取到的电影 id 都存入一个变量中:

import urllib
import json
import time

movie_ids = []
for index in range(0, 250 ,50):
    print index
    response = urllib.urlopen('http://api.douban.com/v2/movie/top250?start=%d&count=50' % index)
    data = response.read()
    # print data

    data_json = json.loads(data)
    movie250 = data_json['subjects']
    for movie in movie250:
        movie_ids.append(movie['id'])
        print movie['id'], movie['title']
    time.sleep(3)
print movie_ids

老师,这里不太理解外面增加一层循环是什么意思。
代码运行后只是前20条在不停重复,怎么办?
作者: crossin先生    时间: 2015-7-12 17:47
lincoln 发表于 2015-7-12 14:20
打印出结果,发现只有 20 条。又查了下文档,原来 Top250 接口还有两个可选参数:start 和 count,表示从第 ...

外层循环是让请求地址里的start参数不断递增,这样每次去请求的结果都是顺着往后的
作者: waterseven    时间: 2016-1-17 22:56
你好,我理解成把豆瓣上的数据抓取下来然后插入数据库,再根据前几节web实战课的内容从数据库中读取电影的数据,补全代码后实现了更多页面的效果。但是不懂为啥我只要更改了运行的主程序doupan.py里的内容,再刷新页面,又会重新等待数据库再导入一次数据。。。。app = web.application(urls,globals())是因为这条语句会把文件中所有函数再执行一遍吗,怎样让它不再重新插入数据呢?

1.jpg (36 KB, 下载次数: 298)

1.jpg


作者: crossin先生    时间: 2016-1-18 13:43
waterseven 发表于 2016-1-17 22:56
你好,我理解成把豆瓣上的数据抓取下来然后插入数据库,再根据前几节web实战课的内容从数据库中读取电影的 ...

你这段添加的代码直接写在了程序里,每次启动都会执行。

如果不需要,可以把它注视掉。或者写在一个方法里,只有访问某个用来刷新的页面时,才去调用它
作者: spritezhong    时间: 2016-3-18 21:49
输出的id信息不对咋办

错误.png (32.52 KB, 下载次数: 321)

错误.png


作者: spritezhong    时间: 2016-3-18 21:54
crossin先生 发表于 2015-5-26 20:13
不知道,你运行了啥,完全没看出原因……

附件的代码我没有看出错误啊
作者: crossin先生    时间: 2016-3-18 23:08
spritezhong 发表于 2016-3-18 21:49
输出的id信息不对咋办

你把这里的movie打印出来看看,好像是movie里面没有id这个属性
作者: AvyMiz    时间: 2016-5-2 15:12
crossin先生 发表于 2016-3-18 23:08
你把这里的movie打印出来看看,好像是movie里面没有id这个属性

先生,这个问题我也遇到了,movie里有id这个属性的。
因为正好是第20个,我一直以为是第一个循环里的问题。后来在思考中断恢复的时候我在想能不能通过movie_id这个list来恢复,结果发现这个中断的原因是第21个电影《搏击俱乐部》的movie[id]是1292000,这个id在API抓取信息的网站上是无效的,即网页:
http://api.douban.com/v2/movie/subject/1292000
的访问结果是:
{"msg":"movie_not_found","code":5000,"request":"GET \/v2\/movie\/subject\/1292000"}
所以无法提取相关的参数。
后来我用蠢方法通过修改movie_id遍历后发现这样有问题的id一共有5个:
搏击俱乐部 1292000
熔炉           5912992
电锯惊魂    1417598
杀人回忆    1300299
罪恶之城    1309027

顺便,先生请问入错出现了这样的中断,该怎么恢复呢?我的方法很没效率,是人工修改程序把这个id之前的id全删掉再重新运行一次。
作者: crossin先生    时间: 2016-5-3 11:14
AvyMiz 发表于 2016-5-2 15:12
先生,这个问题我也遇到了,movie里有id这个属性的。
因为正好是第20个,我一直以为是第一个循环里的问题 ...

通过异常处理,把会出错的代码写在try...except...里面,然后一般会在except里面把出错信息打印或保存下来,然后保证程序能跳过当前问题进入下一次循环
作者: yinxuexia1204    时间: 2016-8-2 11:54
执行没有错误,但是数据库中没有数据,也记得关闭数据库了
作者: crossin先生    时间: 2016-8-2 13:40
yinxuexia1204 发表于 2016-8-2 11:54
执行没有错误,但是数据库中没有数据,也记得关闭数据库了

这个不好说,你要调试来定位。看看是输入的数据不对,还是添加数据库的方式有问题。
把输入的数据print出来检查。
还有就是先用简单的语句测试一下导入数据库是否正常
作者: yinxuexia1204    时间: 2016-8-2 16:39

请教,往数据库插入数据时遇到的报错,不明白咋错了



作者: yinxuexia1204    时间: 2016-8-2 16:48
请教,往数据库中插入时,不知道哪里错了,感觉有单引号很正常
作者: crossin先生    时间: 2016-8-3 09:49
yinxuexia1204 发表于 2016-8-2 16:48
请教,往数据库中插入时,不知道哪里错了,感觉有单引号很正常

光从你这一句话我也不知道哪里错了……
作者: yinxuexia1204    时间: 2016-8-3 09:50
按照老师说的加了start和count,可是执行后,只读到20条数据后就报错:urllib.error.HTTPError: HTTP Error 404: Not Found
这是为什么呀
作者: crossin先生    时间: 2016-8-3 09:54
yinxuexia1204 发表于 2016-8-3 09:50
按照老师说的加了start和count,可是执行后,只读到20条数据后就报错:urllib.error.HTTPError: HTTP Error ...

404这是网页请求失败,没找到对应页面的报错。
你把问题分解开来,去调试定位,把每部分的中间数据输出出来,一部分一部分的排除问题,看哪里不对。不然没法知道哪出错了。
你这么问,真没法知道是什么问题
作者: yinxuexia1204    时间: 2016-8-3 11:14
crossin先生 发表于 2016-8-3 09:54
404这是网页请求失败,没找到对应页面的报错。
你把问题分解开来,去调试定位,把每部分的中间数据输出出 ...

已经找到问题,是取到的ip,第21个没有内容,直接访问也没有内容,找不到,我加了个异常处理,就没问题的,最终取到了249个数据,谢谢老师
作者: qiaoyulong9999    时间: 2016-9-30 08:59
先生,麻烦问一个问题
像前边有位同学说的有几部电影的ID是找不到的,我用了try except语句来处理这种异常
def add_movie(data):
    # print data
    movie = json.loads(data)
    # print movie
    try:
       db.insert('movie',
          id=int(movie['id']),
          title=movie['title'],
          origin=movie['original_title'],
          url=movie['alt'],
          rating=movie['rating']['average'],
          image=movie['images']['large'],
          directors=','.join([d['name'] for d in movie['directors']]),
          casts=','.join([c['name'] for c in movie['casts']]),
          year=movie['year'],
          genres=','.join(movie['genres']),
          countries=','.join(movie['countries']),
          summary=movie['summary'],
       )
       print movie['title']
    except:
       print 'ID is not found'

但是这样出现了一个问题,每次搜索到那个ID找不到的电影的,程序就会从第一部电影开始搜索,这样导致table movie的数据库有很多重复的电影
应该怎么解决,其实应该是在
下边的:
count = 0
for mid in movie_ids:
    # print count, mid
    response = urllib.urlopen('http://api.douban.com/v2/movie/subject/%s' % mid)
    data = response.read()
    add_movie(data)
    count += 1
    time.sleep(3)
加入某些语句,就是遇到ID找不到的电影,直接跳过,开始接下来的搜索,而不是重新开始,但是具体要添加什么语句,望指教,多谢了

作者: crossin先生    时间: 2016-9-30 23:36
qiaoyulong9999 发表于 2016-9-30 08:59
先生,麻烦问一个问题
像前边有位同学说的有几部电影的ID是找不到的,我用了try except语句来处理这种异常
...

1. try catch加的位置不对吧,如果没有找到id,程序之间在抓取的时候就报错了。之所以从头抓,是因为你又刷新或请求了页面吧。所以你应该在会报错的地方加try
2. 在insert之前,你可以查一下这个id的电影是否已经存在,避免重复添加。这样即使中途出错,下次重新运行也不会影响
作者: winmount    时间: 2016-10-31 23:29
请问老师http://api.douban.com/v2/movie/top250为什么在我的chrome上显示成这样

捕获2.PNG (94.2 KB, 下载次数: 329)

捕获2.PNG


作者: crossin先生    时间: 2016-11-1 16:57
winmount 发表于 2016-10-31 23:29
请问老师http://api.douban.com/v2/movie/top250为什么在我的chrome上显示成这样

对的啊,这个接口返回的就是 json 格式的数据。我那个是因为装了调整 json 显示的插件
作者: shmxz1989    时间: 2017-2-4 18:57
本帖最后由 shmxz1989 于 2017-2-4 20:19 编辑

在插入数据的过程中,看提示到了59条的时候就没动静了,也没报错,不知道啥原因
但在之前输出的movie_id中,后面还有好多条数据的。

补更:我把电脑重启了一下,再跑,正常了......

只是最后数据还是没获取齐全,一百来条之后,豆瓣就小气的掐掉了数据,看提示是次数超限了......
但是程序确实是正常的走完了。




作者: crossin先生    时间: 2017-2-5 00:09
shmxz1989 发表于 2017-2-4 18:57
在插入数据的过程中,看提示到了59条的时候就没动静了,也没报错,不知道啥原因
但在之前输出的movie_id中 ...

试试看每次抓取sleep停顿一下,有时太快就会被封
作者: prigioni    时间: 2017-2-19 17:14
spritezhong 发表于 2016-3-18 21:49
输出的id信息不对咋办

我是到第22个也出现这个错误
作者: crossin先生    时间: 2017-2-20 20:59
prigioni 发表于 2017-2-19 17:14
我是到第22个也出现这个错误

有时是爬太快被暂时封掉,有时是请求卡住了。也可能遇到某部电影的信息不开放
作者: nekonekobox    时间: 2017-3-15 01:05
prigioni 发表于 2017-2-19 17:14
我是到第22个也出现这个错误

试了一下终于解决了只能拉到22个的问题,原因在于Top250拉到的列表里有个id有问题
使用Try……else即可解决
在下面这个语句里加上Try else,如下,这也提了个醒,以后要养成好习惯,请求网址的时候要养成习惯使用try else语句,防止由于网址打不开后面的逻辑都走不通
count=0
for mid in movie_ids:
    print(count,mid)
    try:
        response=request.urlopen('http://api.douban.com/v2/movie/subject/%s'%mid)
        data=response.read().decode('utf-8')
        add_movie(data)
        count+=1
        time.sleep(3)
    except:
        print('id is not found')
作者: nekonekobox    时间: 2017-3-15 01:29
我的ip被豆瓣封掉了,咋办,大概30多个的时候不行了
企业微信截图_20170315012808.png
企业微信截图_20170315012944.png



作者: crossin先生    时间: 2017-3-15 17:09
nekonekobox 发表于 2017-3-15 01:29
我的ip被豆瓣封掉了,咋办,大概30多个的时候不行了

请求太快了吧。过一阵子应该会解封,之后再抓记得每次停一会儿
作者: 万建涛    时间: 2017-3-23 13:13
AvyMiz 发表于 2016-5-2 15:12
先生,这个问题我也遇到了,movie里有id这个属性的。
因为正好是第20个,我一直以为是第一个循环里的问题 ...

你是怎么解决的这个问题,我也是同样的问题
作者: crossin先生    时间: 2017-3-23 17:34
万建涛 发表于 2017-3-23 13:13
你是怎么解决的这个问题,我也是同样的问题

有些id是抓不到的。用异常处理跳过这些电影
作者: 不会飞的hjh    时间: 2017-9-10 12:43
先生你好,可能是现在失效的id更多,抓取到的也是大概100多部,但是效率比较低,所以我按照之前的课程加入多线程,这样时间是减少了,可是为什么抓取下来,只成功了几部而已呢?

多线程代码.png (15.06 KB, 下载次数: 306)

多线程代码.png


作者: crossin先生    时间: 2017-9-10 15:14
不会飞的hjh 发表于 2017-9-10 12:43
先生你好,可能是现在失效的id更多,抓取到的也是大概100多部,但是效率比较低,所以我按照之前的课程加入 ...

没有任何输出和调试信息,无法仅从你的代码看出

可能是因为你没用 join,所以程序直接在创建完所有线程后就结束了
作者: 不会飞的hjh    时间: 2017-9-12 09:56
crossin先生 发表于 2017-9-10 15:14
没有任何输出和调试信息,无法仅从你的代码看出

可能是因为你没用 join,所以程序直接在创建完所有线程 ...

如果说只是怕主程序提前退出的话,用先生之前的加入一行input语句就应该可以搞定,试了下也是没效果(在for外面),关于join的用法,查询到应该是在所有的线程都开启后,逐个join,这样一来,线程的变量名就要逐个定义,感觉应该不是这样的用法,理解的有点浅,还望先生指教

对 关于报错的,在执行的时候会有这样的ERR错误,其余都无报错

20170911_212344.png (18.35 KB, 下载次数: 302)

20170911_212344.png


作者: crossin先生    时间: 2017-9-12 11:51
不会飞的hjh 发表于 2017-9-12 09:56
如果说只是怕主程序提前退出的话,用先生之前的加入一行input语句就应该可以搞定,试了下也是没效果(在fo ...

把你的try去掉,或者在except里输出报错信息,不然你这个看到的都是你自己输出的 error,把真实error覆盖了

你这个是请求失败了,具体原因被你的try给隐藏了
是不是你请求太快,豆瓣每分钟最多40次


作者: 不会飞的hjh    时间: 2017-9-12 12:38
crossin先生 发表于 2017-9-12 11:51
把你的try去掉,或者在except里输出报错信息,不然你这个看到的都是你自己输出的 error,把真实error覆盖 ...

请求太快会直接在我运行代码的时候就报错,添加了报错信息之后发现,显示为database is locked,然后查询了一下发现是sqlite 对多线程并发不是很支持,尤其是并发写入
作者: crossin先生    时间: 2017-9-13 11:04
不会飞的hjh 发表于 2017-9-12 12:38
请求太快会直接在我运行代码的时候就报错,添加了报错信息之后发现,显示为database is locked,然后查询 ...

可以换mysql
作者: 13253620252    时间: 2017-11-26 21:19
先生,我有两个问题。
一是:movie250=data_json['subjects']是不是每循环一次,movie250的内容都只是当前捕获50条movie的内容?下一次会覆盖上一次的?
二是:movie_ids这个list中的250条数据是不是执行上述代码后获取的每一个ID数据?

捕获.PNG (14.12 KB, 下载次数: 287)

捕获.PNG


作者: crossin先生    时间: 2017-11-27 01:35
13253620252 发表于 2017-11-26 21:19
先生,我有两个问题。
一是:movie250=data_json['subjects']是不是每循环一次,movie250的内容都只是当前 ...

是的

这个你其实通过输出就可以看出,不清楚的变量都可以输出出来看
作者: 13253620252    时间: 2017-11-30 19:52
crossin先生 发表于 2017-11-27 01:35
是的

这个你其实通过输出就可以看出,不清楚的变量都可以输出出来看

先生,这里抓取到的movie_ids中,为什么是用u'  ’表示呢?
作者: braid    时间: 2017-12-1 17:19
13253620252 发表于 2017-11-30 19:52
先生,这里抓取到的movie_ids中,为什么是用u'  ’表示呢?

这个u表示将后面跟的字符串以unicode格式存储
作者: 小可爱    时间: 2017-12-4 19:13
  =-=python3.6,web模块模式没有了,安装也总失败,flask也装不上QAQ
import urllib也变成import urllib.request
=-=萌新好头大哟
作者: TED    时间: 2017-12-4 19:35
小可爱 发表于 2017-12-4 19:13
=-=python3.6,web模块模式没有了,安装也总失败,flask也装不上QAQ
import urllib也变成import urllib.re ...

我之前用python3.7 web.py不行,CherryPy也失败,tornado也安装失败。。今天下午卸载了3.7 现在安3.6也失败。。。要崩溃了
作者: zhaolehua    时间: 2017-12-14 11:32
先生,我在虚拟机上面运行了web.py,运行的终端就相当于一个服务器吗? 我能在虚拟机以外的网络访问这个电影网站吗?比如我在运行虚拟机的主机上好像就不能打开那个网站了。
作者: crossin先生    时间: 2017-12-14 14:18
zhaolehua 发表于 2017-12-14 11:32
先生,我在虚拟机上面运行了web.py,运行的终端就相当于一个服务器吗? 我能在虚拟机以外的网络访问这个电 ...

你要确保网络是连通的,虚拟机的网络有几种设置,如果是选择作为局域网中的一台电脑,有独立内网ip,就可以访问。桥接模式不行
作者: zhaolehua    时间: 2017-12-14 14:39
crossin先生 发表于 2017-12-14 14:18
你要确保网络是连通的,虚拟机的网络有几种设置,如果是选择作为局域网中的一台电脑,有独立内网ip,就可 ...

难怪咯,我是用的桥接模式。
作者: qiqibaoer    时间: 2018-2-6 17:11
crossin先生 发表于 2015-6-2 23:11
是不是数据库没抓全?你去sqlite里面看看数据

我也有遇到,抓取数据时会有找不到数据的情况,然后程序就停止了,这种不知道怎么处理下跳过该数据继续执行呢?
作者: crossin先生    时间: 2018-2-7 17:02
qiqibaoer 发表于 2018-2-6 17:11
我也有遇到,抓取数据时会有找不到数据的情况,然后程序就停止了,这种不知道怎么处理下跳过该数据继续执 ...

异常处理
作者: 风扇很响    时间: 2018-5-19 17:18
#-*-coding: utf-8 -*-
#D:\ProgramData\Anaconda3\envs\py36\python.exe code.py 127.0.0.1
import web
import urllib.request
import json
import time

urls = (
    '/', 'index',
    '/movie/(\d+)', 'movie',
)

render = web.template.render('templates/')

#web.py 连接 SQLite
db = web.database(dbn='sqlite', db='MovieSite.db')

def add_movie(data):
    movie = json.loads(data)
    #print (movie['title'])
db.insert('movie',
        id = int(movie['id']),
        title = movie['title'],
        origin = movie['original_title'],
        url = movie['alt'],
        rating = movie['rating']['average'],
        image = movie['images']['large'],
        directors = ','.join([d['name'] for d in movie['directors']]),
        casts = ','.join([c['name'] for c in movie['casts']]),
        year = movie['year'],
        genres = ','.join(movie['genres']),
        countries = ','.join(movie['countries']),
        summary = movie['summary'],
    )

def movie_exist(data):
    movie = json.loads(data)
    n_id = int(movie['id'])
    pass
class index:
    def GET(self):
        movies = db.select('movie')
        return render.index(movies)

    def POST(self):
        data = web.input()
        condition = r'title like "%' + data.title + r'%"'
movies = db.select('movie', where=condition)
        return render.index(movies)

class movie:
    def GET(self, movie_id):
        condition = 'id=' + movie_id
        movie = db.select('movie', where=condition)[0]
        return render.movie(movie)


movie_ids = []
for index in range(0, 250, 50):
    response = urllib.request.urlopen('http://api.douban.com/v2/movie/top250?start=%d&count=50' % index)
    data = response.read()
    data_json = json.loads(data)
    movie250 = data_json['subjects']
    for movie in movie250:
        movie_ids.append(movie['id'])
        #print(movie['id'], movie['title'])
time.sleep(3)
#print(movie_ids)
count = 0
for mid in movie_ids:
    #print (count, mid)
try:
        response = urllib.request.urlopen('http://api.douban.com/v2/movie/subject/%s' % mid)
        data = response.read()
        add_movie(data)
        count += 1
        time.sleep(3)
    except:
        print('movie %s is not found' % mid)

if __name__ == "__main__":
    app = web.application(urls, globals())
    app.run()

第一次从头开始sqlite3 MovieSite.db
create table movie (id, title, origin, url, rating, image, directors, casts, year, genres, countries, summary);然后运行上面的程序,进入网页报错
无标题5.jpg 无标题4.jpg
第二次把获取movie_ids和存入数据库的两段注释掉,再运行,就可以正常显示网页了(虽然也没全部抓取到,只有90多个)
麻烦老师看下这会是什么问题?




作者: crossin先生    时间: 2018-5-19 17:30
风扇很响 发表于 2018-5-19 17:18
#-*-coding: utf-8 -*-
#D:\ProgramData\Anaconda3\envs\py36\python.exe code.py 127.0.0.1
import web

可能只是达到豆瓣接口限制被暂时拒绝了

你要调试的话,把输出的url 给 print 出来看一下
作者: 风扇很响    时间: 2018-5-20 20:29
crossin先生 发表于 2018-5-19 17:30
可能只是达到豆瓣接口限制被暂时拒绝了

你要调试的话,把输出的url 给 print 出来看一下 ...

一头雾水...
稍微改了下想减少点数量,调试方便一点
#-*-coding: utf-8 -*-
#D:\ProgramData\Anaconda3\envs\py36\python.exe code.py 127.0.0.1
import web
import urllib.request
import json
import time

urls = (
    '/', 'index',
    '/movie/(\d+)', 'movie',
)

render = web.template.render('templates/')

#web.py 连接 SQLite
db = web.database(dbn='sqlite', db='MovieSite.db')

def add_movie(data):
    movie = json.loads(data)
    #print (movie['title'])
    db.insert('movie',
        id = int(movie['id']),
        title = movie['title'],
        origin = movie['original_title'],
        url = movie['alt'],
        rating = movie['rating']['average'],
        image = movie['images']['large'],
        directors = ','.join([d['name'] for d in movie['directors']]),
        casts = ','.join([c['name'] for c in movie['casts']]),
        year = movie['year'],
        genres = ','.join(movie['genres']),
        countries = ','.join(movie['countries']),
        summary = movie['summary'],
    )

def movie_exist(data):
    movie = json.loads(data)
    n_id = int(movie['id'])
    pass

class index:
    def GET(self):
        movies = db.select('movie')
        return render.index(movies)

    def POST(self):
        data = web.input()
        condition = r'title like "%' + data.title + r'%"'
        movies = db.select('movie', where=condition)
        return render.index(movies)

class movie:
    def GET(self, movie_id):
        condition = 'id=' + movie_id
        movie = db.select('movie', where=condition)[0]
        return render.movie(movie)

def get_movie_ids(total_num, step_num):
    movie_ids = []
    for index in range(0, total_num, step_num):
        response = urllib.request.urlopen('http://api.douban.com/v2/movie/top250?start=%d&count=step_num' % index)
        data = response.read()
        data_json = json.loads(data)
        movie_total = data_json['subjects']
        #print(len(movie_total))
        for movie in movie_total:
            movie_ids.append(movie['id'])
            print(movie['id'], movie['title'])
        time.sleep(3)
    #print(movie_ids)
    #print(len(movie_ids))
    return  movie_ids

def store_movie(movie_ids):
    count = 0
    for mid in movie_ids:
        #print (count, mid)
        try:
            response = urllib.request.urlopen('http://api.douban.com/v2/movie/subject/%s' % mid)
            data = response.read()
            add_movie(data)
            count += 1
            time.sleep(3)
        except:
            print('movie %s is not found' % mid)

movie_ids = get_movie_ids(30, 10)
store_movie(movie_ids)

if __name__ == "__main__":
    app = web.application(urls, globals())
    app.run()

第一次运行,看到数据库已经导入记录成功了,终端里显示http://127.0.0.1:8080/
但是浏览器打开网页之后,页面一直没有显示,这时候看到终端里又自动重新运行了一遍,直到被豆瓣接口拒绝
老师能不能看下这是什么问题?

作者: crossin先生    时间: 2018-5-21 10:57
风扇很响 发表于 2018-5-20 20:29
一头雾水...
稍微改了下想减少点数量,调试方便一点
#-*-coding: utf-8 -*-

movie_ids = get_movie_ids(30, 10)
store_movie(movie_ids)

你这两句放在代码里,每次运行都会去执行抓取任务
作者: 风扇很响    时间: 2018-5-21 20:26
crossin先生 发表于 2018-5-21 10:57
movie_ids = get_movie_ids(30, 10)
store_movie(movie_ids)

好了,现在差不多了,把movie_ids的结果放到一个文件里,每次从文件读取,如果文件不存在再做get_movie_ids
不过MovieSite.db里仍然可能重复保存,浪费时间,而且弄不好又被豆瓣封掉了,我找找怎么样可以做db.insert之前先检测一下相同的id是否已经存在
作者: crossin先生    时间: 2018-5-21 22:57
风扇很响 发表于 2018-5-21 20:26
好了,现在差不多了,把movie_ids的结果放到一个文件里,每次从文件读取,如果文件不存在再做get_movie_i ...

insert之前先做一次select
作者: boat    时间: 2019-8-18 08:41
这节课没啥问题,主要是两个地方注意一下。
一是python3里要用urllib.request.urlopen();
二是目前豆瓣API已经关闭了,可以尝试通过下面两种进行连接
http://www.imooc.com/qadetail/319172
https://www.xiaojianjian.net/archives/4559
作者: crossin先生    时间: 2019-8-20 22:23
boat 发表于 2019-8-18 08:41
这节课没啥问题,主要是两个地方注意一下。
一是python3里要用urllib.request.urlopen();
二是目前豆瓣API ...

赞一个




欢迎光临 Crossin的编程教室 (https://bbs.crossincode.com/) Powered by Discuz! X2.5