背景最近刚学了 1. 抓取电影列表页先定为到列表页http:///html/gndy/dyzz/list_23_1.html,这个网址是最新电影的第一页,替换网址最后面的1为2、3、4…直到166就得到了电影列表的所有页面,默认是数字越大电影上映时间越久。 使用 # 请求url页面def do_request(url): try: headers = { 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8', 'Accept-Language': 'zh-CN,zh;q=0.8', 'Host': 'www.', 'Connection': 'keep-alive', 'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64)' } resp = requests.get(url, headers=headers, timeout=10) resp.encoding = 'gb18030' return resp.text except requests.exceptions.RequestException as e: return ''# 提取影片urldef fetch_movie_urls(filename): if not os.path.isfile(filename): with open(filename, 'w') as file_url: base_url = 'http://www./html/gndy/dyzz/list_23_{0}.html' for page in range(1, 166): html = do_request(base_url.format(str(page))) urls = extract_urls(html) file_url.write(urls) 代码中的 import re# 正则表达式r_url = re.compile('.*?', re.DOTALL)# 提取电影urldef extract_urls(html): result = '' segments = r_url.findall(html) host = 'http://www.' for seg in segments: result = result + host + seg + '\n' return result 执行 2. 抓取详情页提取有效信息访问上一步骤文本中具体的一个电影详情url就进入到电影详情页面,在这个页面我们可以提取到电影的具体信息,包括电影名称、语言、国家、上映时间、简介、演员、下载地址等等。 提取这些信息的正则表达式如下: import re# 匹配影片详细信息r_name_cn = re.compile(u'◎译 名(.*?) 3. 多线程执行爬取操作上面第一个步骤已经得到所有电影的详情地址,第二个步骤提取到具体某一个电影的详细信息,如果使用单线程一个个的去取也能得到全部信息,但是网络操作大部分时间都是在等待 多线程爬取得到的结果先写到文件中,为了保证写操作同步,考虑使用两个队列,第一个输入队列用来存储第一步得到的所有电影详情的url,第二个输出队列用来存储第二个步骤得到的电影详情信息,然后启动单独的写线程不断的从输出队列读取信息向同一文件中写入电影详情信息,直到写线程从输出队列读取到退出元素为止。 # 工作线程class Spider(threading.Thread): ''' 1.从输入队列中提取电影详情url请求并提取有效信息 2.将有效信息放入输出队列中供输出线程记录 ''' def __init__(self, in_queue, out_queue): super(Spider, self).__init__() self.in_queue = in_queue self.out_queue = out_queue def run(self): while True: if self.in_queue.empty(): logger.debug('in_queue empty.{0} exiting...'.format(threading.current_thread().getName())) break else: url = self.in_queue.get() if url: result = Spider.process(url) self.out_queue.put(result) @staticmethod def process(url): html = do_request(url.strip()) return extract_details(html) # 记录线程class Writer(threading.Thread): ''' 输出线程:不断从队列中取出处理完的信息并记录到文件 直到取到的元素为退出标识为止 ''' __exit = True def __init__(self, queue, filename): super(Writer, self).__init__() self.queue = queue self.filename = filename def stop(self): self.queue.put(self.__exit) def run(self): with open(self.filename, 'wb') as f: while True: data = self.queue.get() if not data: continue if data is self.__exit: break f.write(data.encode('utf8')) logger.debug('write file:{0}'.format(data)) 执行: def main(): # 电影地址文件 path_urls = os.path.join(directory, 'movie_urls.txt') # 电影详情文件 path_movies = os.path.join(directory, 'movies.txt') start = time.time() in_queue = Queue() out_queue = Queue() with open(path_urls, 'r') as f: lines = f.readlines() for line in lines: in_queue.put(line) # 启动记录线程 writer = Writer(out_queue, filename=path_movies) writer.start() # 启动爬虫线程 spiders = [Spider(in_queue, out_queue) for i in range(20)] for s in spiders: s.start() for s in spiders: s.join() # 爬虫线程结束,向输出线程发出结束信号 writer.stop() writer.join() logger.debug('all done({0}s).'.format(time.time() - start)) 4. 导入数据库上面几个步骤得到了一个包含所有电影信息的有固定格式的文本文:一条电影占一行,不同字段以 5. 成果源码地址:https://github.com/lonnyzhang423/dytt.git |
|