requests+xpath爬取电影天堂电影信息

电影天地网址:http://www.ygdy8.net/html/gndy/china/list_4_1.html
目标
1.爬取电影天堂的国内电影一栏的所有电影的url
2.进入每个电影的url获取电影的信息

使用到的库:
请求获取资源:requests库
配合xpath使用的库:from lxml import etree
定位数据:xpath
系统文件相关的I/O操作:os库
创建线程使用到的库:threading
线程进程的理解:https://blog.csdn.net/weixin_43919632/article/details/89209638

值得注意的:

重要的问题打五角星哦 ☆ 在这里插入图片描述
1.☆编码问题:
用requests库get网页时候出现:(查看了网页源代码:使用的编码方式为GB2313---------gbk的一种),所以我使用gbk方式编码,但是网页都打印不出来,后来我换成utf-8,网页还是打印不出来。经过网上一番搜寻得知:这类网页不规范,网页上的有些字符不是规范的,即一个网页上使用了多种不同类的字符,造成不管用gbk还是utf-8都会出现乱码。
编码类问题通用解决方法:
先自定义编码,再相应的解码,同时忽略非法字符,(很重要)。

import requests

res=requests.get(url)
text=res.content.decode("utf-8","ignore")

这里说明一下:content表示获取网页的原字符的文本,即字节形式的文本。
而 res.text是requests库采取默认的解码方式来解码得到的网页,这样难免会出错。

使用到的知识点:

1.map函数的使用
2.lambda函数的使用
3.enumerate函数的使用
4.字典中实现一个键对应多个值
5.线程的创建
6.startswith-----判断字符串的开头
7.replace和strip方法使用

1.map函数使用:
map函数允许传入两个参数,一个函数和一个列表。列表中的每个元素都执行传入的这个参数,最后返回一个新的列表。

def func(x):
    return x*x
printmap(func, [1, 2, 3, 4, 5, 6, 7, 8, 9]))
输出结果:
[1, 4, 9, 10, 25, 36, 49, 64, 81]

2.lambda函数的使用,常和map函数配合使用:

list=[1,2,3,4,5]
#" :"前面的“x“”是指list中的每一个元素,“:”后面的表示对列表中每个元素进行操作的表达式
result=map(lambda x:x**2,list)
print(result)
结果:
[1,4,9,16,25]

3.enumerate函数使用:

list=["hello","world","Alice","Ben"]
for index,i in enumerate("list")
	print(index,i)
结果:
0 hello
1 world
2 Alice
3 Ben
显而易见:其作用是返回列表中的每个元素的索引位置。

enumerate参考菜鸟教程:http://www.runoob.com/python/python-func-enumerate.html
4.一个键对应多个值思想方法:将这个键对应的所有值用列表装起来即可,再把这个列表赋给这个键


film={}
actors=[]
for i in range(index+1,len(other_infos)):
	actor=info[i].strip()
	if actor.startswith("◎"):
           break
    actors.append(actor)
    film["主演"]=actors

5.线程的创建:https://blog.csdn.net/weixin_43919632/article/details/89209638
6.startswith使用:

str = "this is string example....wow!!!"
if str.startswith( 'this' ))  # 字符串是否以 this 开头
	print("是this开头")
elseprint("不是")
if (str.startswith( 'string', 8 )):  # 从第八个字符开始的字符串是否以 string 开头
	print(“是”)
else:
	print("不是")
if(str.startswith( 'this', 2, 4 )): # 从第2个字符开始到第四个字符结束的字符串是否以 this 开头
	print("是")
elseprint("不是")
结果:
是
是
不是
	

7.strip方法菜鸟参考:http://www.runoob.com/python3/python3-string-strip.html
replace方法菜鸟参考:http://www.runoob.com/python3/python3-string-replace.html

爬取电影天堂电影信息全代码:

 #encoding:utf-8
import requests
from lxml import etree
import threading
import os

"""
warning:网页出现 不管用gbk或是utf-8都出现乱码问题时,需要我们先编码再解码,同时忽略掉非法字符
"""

HEADERS = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36"}
BaseDomain="http://www.ygdy8.net"


#get all flm_url  of every_page
def get_detail_urls(page_url):
    re=requests.get(page_url,headers=HEADERS)
    lxml=etree.HTML(re.content.decode("gbk","ignore"))
    flm_urls=lxml.xpath("//table[@class='tbspan']//a[2]/@href")
    """
    1.what the map function does is it allows two params a funciton and a list ,
    finally it returns a new list that has been processed by the function
    2.lambda function: the word before":" means it's a params,after the":"means it's a 
    funcitonal expression and it will be returned
    3. the params "url" in the map is one in flm_urls list
    """
    flm_urls=map(lambda url:BaseDomain+url,flm_urls)
    return flm_urls

def get_flm_infos(flm_url):
    film={}
    global flm_name
    res = requests.get(flm_url, headers=HEADERS)
    html = etree.HTML(res.content.decode("gbk", "ignore"))
    try:
        flm_name = html.xpath("//div[@class='title_all']//font/text()")[0]
        zoom = html.xpath("//div[@id='Zoom']")[0]
        imgs = zoom.xpath(".//img/@src")
        cover = imgs[0]
        screenshot = imgs[1]
        download_url = html.xpath("//td/a/@href")[0]
        other_infos=zoom.xpath(".//text()")
        film["电影名"]=flm_name
        film["电影下载地址"]=download_url
        film["海报"]=cover
        film["电影截图"]=screenshot
        #the enumerate funciton : the param-index means  index,order . the second param-info
        #is one in other_infos
        for index, info in enumerate(other_infos):

            if info.startswith("◎年代"):
                info=info.replace("◎年代","").strip()
                film["年代"]=info
            elif info.startswith("◎产  地"):
                info=info.replace("◎产  地","").strip()
                film["产地"]=info
            elif info.startswith("◎类  别"):
                info=info.replace("◎类  别","").strip()
                film["类别"]=info
            elif info.startswith("◎语  言"):
                info=info.replace("◎语  言","").strip()
                film["语言"]=info
            elif info.startswith("◎上映日期"):
                info=info.replace("◎上映日期","").strip()
                film["上映日期"]=info
            elif info.startswith("◎豆瓣评分"):
                info=info.replace("◎豆瓣评分","").strip()
                film["豆瓣评分"]=info
            elif info.startswith("◎片  长"):
                info=info.replace("◎片  长","").strip()
                film["片长"]=info
            elif info.startswith("◎导  演 "):
                info=info.replace("◎导  演","").strip()
                film["导演"]=info
            elif info.startswith("◎编  剧"):
                info=info.replace("◎编  剧","").strip()
                film["编剧"]=info
            elif info.startswith("◎主  演"):
                info=info.replace("◎主  演","").strip()
                """
                实现字典一个键 对应多个值:
                """
                actors=[info]
                for i in range(index+1,len(other_infos)):
                    actor=other_infos[i].strip()
                    if actor.startswith("◎"):
                        break
                    actors.append(actor)
                    film["主演"]="".join(actors)
                """
                网页源代码里发现,"简介"是单独一行的,简介的内容是下一行.
                用enumerate获取简介内容的下标,利用列表取值方法,获得简介内容
                """
            elif info.startswith("◎简  介 "):
                    info=other_infos[index+1].strip()
                    film["电影简介"]=info

    except:
        pass

    return film


def film_spider():
    # roll page and get page_url
    for i in range(1, 2):
        page_url = "http://www.ygdy8.net/html/gndy/china/list_4_{}.html".format(i)
        flm_urls = get_detail_urls(page_url)
        for flm_url in flm_urls:
            #print(flm_url)
            film=get_flm_infos(flm_url)
            #print(film)
            save_films(film)
            print(flm_name,"保存完成")

#save all_films
def save_films(film):
    path="C:\\Users\\Zhangchuan\\Pictures\\电影天堂电影信息"
    if not os.path.exists(path):
        os.mkdir(path)
    file_name=path+"\\"+"电影天堂电影信息.txt"
    with open(file_name,"a",encoding="utf-8") as f:
        for (key,value) in film.items():
            #每写入一个键值对就换一行
            f.write("{}:{} \n".format(key,value))
        #一个电影写完后就换两行
        f.write("\n\n")



if __name__=="__main__":
    #establish  16 threads
    for i in range(1):
        tl=threading.Thread(target=film_spider())
        tl.start()








评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值