<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Anooj Rajamohan, Author at Be on the Right Side of Change</title>
	<atom:link href="https://blog.finxter.com/author/anoojrajamohan/feed/" rel="self" type="application/rss+xml" />
	<link>https://blog.finxter.com/author/anoojrajamohan/</link>
	<description></description>
	<lastBuildDate>Sat, 01 Oct 2022 14:32:49 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=7.1.3</generator>

<image>
	<url>https://blog.finxter.com/wp-content/uploads/2020/08/cropped-cropped-finxter_nobackground-32x32.png</url>
	<title>Anooj Rajamohan, Author at Be on the Right Side of Change</title>
	<link>https://blog.finxter.com/author/anoojrajamohan/</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>Python Scrapy &#8211; Scraping Dynamic Website with API-Generated Content</title>
		<link>https://blog.finxter.com/python-scrapy-scraping-dynamic-website-with-api-generated-content/</link>
		
		<dc:creator><![CDATA[Anooj Rajamohan]]></dc:creator>
		<pubDate>Sat, 01 Oct 2022 14:32:47 +0000</pubDate>
				<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[Scripting]]></category>
		<category><![CDATA[Web Scraping]]></category>
		<guid isPermaLink="false">https://blog.finxter.com/?p=742150</guid>

					<description><![CDATA[<p>Scrapy is an excellent tool for extracting data from static and dynamic websites. In this article, we are going to discuss the solution to the following problems: Extract all details of Offices from the website https:/directory.ntschools.net/#/offices Instead of using a whole scrapy framework, use a typical Python script for extracting the data. For each office, ... <a title="Python Scrapy &#8211; Scraping Dynamic Website with API-Generated Content" class="read-more" href="https://blog.finxter.com/python-scrapy-scraping-dynamic-website-with-api-generated-content/" aria-label="Read more about Python Scrapy &#8211; Scraping Dynamic Website with API-Generated Content">Read more</a></p>
<p>The post <a href="https://blog.finxter.com/python-scrapy-scraping-dynamic-website-with-api-generated-content/">Python Scrapy &#8211; Scraping Dynamic Website with API-Generated Content</a> appeared first on <a href="https://blog.finxter.com">Be on the Right Side of Change</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph"><a href="https://blog.finxter.com/how-to-run-a-scrapy-spider-from-a-python-script/" data-type="post" data-id="28124" target="_blank" rel="noreferrer noopener">Scrapy</a> is an excellent tool for extracting data from static and dynamic websites. </p>



<p class="wp-block-paragraph">In this article, we are going to discuss the solution to the following problems:</p>



<ol class="wp-block-list"><li>Extract all details of Offices from the website https:/directory.ntschools.net/#/offices</li><li>Instead of using a whole scrapy framework, use a typical Python script for extracting the data.</li><li>For each office, we need to scrape the Physical address, Postal address, Phone, Parent Division, and Email.</li><li>Also, to scrape the data by running the python script file instead of crawling scrapy from the terminal.</li><li>Create a Dataframe for the office details scraped from the website and print the output.</li></ol>



<h2 class="wp-block-heading">Extract all details of Offices</h2>



<p class="wp-block-paragraph">First, let&#8217;s quickly look at the website for data extraction.</p>



<figure class="wp-block-image size-full"><img fetchpriority="high" decoding="async" width="1013" height="816" src="https://blog.finxter.com/wp-content/uploads/2022/10/image.png" alt="" class="wp-image-742166" srcset="https://blog.finxter.com/wp-content/uploads/2022/10/image.png 1013w, https://blog.finxter.com/wp-content/uploads/2022/10/image-300x242.png 300w, https://blog.finxter.com/wp-content/uploads/2022/10/image-768x619.png 768w" sizes="(max-width: 1013px) 100vw, 1013px" /></figure>



<figure class="wp-block-image size-large"><img decoding="async" width="1024" height="770" src="https://blog.finxter.com/wp-content/uploads/2022/10/image-1-1024x770.png" alt="" class="wp-image-742170" srcset="https://blog.finxter.com/wp-content/uploads/2022/10/image-1-1024x770.png 1024w, https://blog.finxter.com/wp-content/uploads/2022/10/image-1-300x226.png 300w, https://blog.finxter.com/wp-content/uploads/2022/10/image-1-768x577.png 768w, https://blog.finxter.com/wp-content/uploads/2022/10/image-1.png 1035w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>



<p class="wp-block-paragraph">Now disable the <a href="https://blog.finxter.com/javascript-developer-income-and-opportunity/" data-type="post" data-id="191233" target="_blank" rel="noreferrer noopener">JavaScript</a> in the web browser and reload the page. </p>



<p class="wp-block-paragraph">We can see that nothing is loading in the browser. That means the entire website is running by JavaScript. So regular scraping will not get data from this website. </p>



<p class="wp-block-paragraph">We can get the data using <a rel="noreferrer noopener" href="https://blog.finxter.com/how-to-refresh-backward-forward-a-page-with-selenium-in-python/" data-type="post" data-id="36862" target="_blank">selenium</a>, <a rel="noreferrer noopener" href="https://blog.finxter.com/how-to-install-scrapy-splash-in-python/" data-type="post" data-id="324639" target="_blank">splash</a>, or other methods to fetch the javascript-loaded data. </p>



<p class="wp-block-paragraph">But before going for any of those solutions, we can go to the developer tab, under section network, and check for the network flow during the site loading. You can see a lot of activities are going under the network section. </p>



<p class="wp-block-paragraph">Now we have to filter the content using the XHR tab. So we can see two items under the network section.</p>



<figure class="wp-block-image size-large"><img decoding="async" width="1024" height="518" src="https://blog.finxter.com/wp-content/uploads/2022/10/image-1024x518.jpeg" alt="" class="wp-image-742179" srcset="https://blog.finxter.com/wp-content/uploads/2022/10/image-1024x518.jpeg 1024w, https://blog.finxter.com/wp-content/uploads/2022/10/image-300x152.jpeg 300w, https://blog.finxter.com/wp-content/uploads/2022/10/image-768x388.jpeg 768w, https://blog.finxter.com/wp-content/uploads/2022/10/image-1536x777.jpeg 1536w, https://blog.finxter.com/wp-content/uploads/2022/10/image.jpeg 1600w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>



<p class="wp-block-paragraph">Select the `GetOfficeList` item then we can see more details of that item&#8217;s content. </p>



<p class="wp-block-paragraph">Under the new section, we can see the tabs of Headers, Preview, Response, etc. </p>



<p class="wp-block-paragraph">Select the Response tab, and then we can see the required details included in this link as a JSON object.</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="573" src="https://blog.finxter.com/wp-content/uploads/2022/10/image-1-1024x573.jpeg" alt="" class="wp-image-742187" srcset="https://blog.finxter.com/wp-content/uploads/2022/10/image-1-1024x573.jpeg 1024w, https://blog.finxter.com/wp-content/uploads/2022/10/image-1-300x168.jpeg 300w, https://blog.finxter.com/wp-content/uploads/2022/10/image-1-768x430.jpeg 768w, https://blog.finxter.com/wp-content/uploads/2022/10/image-1-1536x860.jpeg 1536w, https://blog.finxter.com/wp-content/uploads/2022/10/image-1.jpeg 1600w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<p class="wp-block-paragraph">We can get all the required data by getting this <a rel="noreferrer noopener" href="https://blog.finxter.com/parse-json-data-in-python/" data-type="post" data-id="197286" target="_blank">JSON</a> object from the particular request URL. </p>



<p class="wp-block-paragraph">To get that URL, we can right-click on the GetOfficeList, select the copylinkaddress and store it in a variable named `<code>base_url</code>`. </p>



<p class="wp-block-paragraph">To make a request, we require the request header details. Select the copy request headers option and store the data by converting it into a python dictionary.</p>


<div class="wp-block-image">
<figure class="aligncenter size-full"><img loading="lazy" decoding="async" width="656" height="412" src="https://blog.finxter.com/wp-content/uploads/2022/10/image-2.png" alt="" class="wp-image-742192" title="Request headers content" srcset="https://blog.finxter.com/wp-content/uploads/2022/10/image-2.png 656w, https://blog.finxter.com/wp-content/uploads/2022/10/image-2-300x188.png 300w" sizes="auto, (max-width: 656px) 100vw, 656px" /></figure>
</div>


<p class="wp-block-paragraph">Request Headers Contents</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="799" src="https://blog.finxter.com/wp-content/uploads/2022/10/image-2-1024x799.jpeg" alt="" class="wp-image-742195" srcset="https://blog.finxter.com/wp-content/uploads/2022/10/image-2-1024x799.jpeg 1024w, https://blog.finxter.com/wp-content/uploads/2022/10/image-2-300x234.jpeg 300w, https://blog.finxter.com/wp-content/uploads/2022/10/image-2-768x600.jpeg 768w, https://blog.finxter.com/wp-content/uploads/2022/10/image-2-1536x1199.jpeg 1536w, https://blog.finxter.com/wp-content/uploads/2022/10/image-2.jpeg 1600w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<h2 class="wp-block-heading">A Typical Python Script for Extracting the Data</h2>



<p class="wp-block-paragraph">Now we have everything to fetch the data from the website. Let’s start the <a href="https://blog.finxter.com/how-to-run-a-scrapy-spider-from-a-python-script/" data-type="post" data-id="28124" target="_blank" rel="noreferrer noopener">spider</a> for scraping the website.</p>



<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">(venv) $ scrapy genspider office_details domain</pre>



<p class="wp-block-paragraph">This will generate a spider basic template file.</p>



<pre class="EnlighterJSRAW" data-enlighter-language="python" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">(venv) $ cat office_details.py
import scrapy


class OfficeDetailsSpider(scrapy.Spider):
    name = 'office_details'
    allowed_domains = ['domain']
    start_urls = ['http://domain/']

    def parse(self, response):
        Pass
</pre>



<p class="wp-block-paragraph">In this, we are removing the <code>allowed_domains</code> variable since we are not using it. We have to include the website URL (“<a rel="noreferrer noopener" href="https://directory.ntschools.net/#/offices" target="_blank">https://directory.ntschools.net /#/offices</a>”) in the <code>strat_urls</code> <a href="https://blog.finxter.com/python-lists/" data-type="post" data-id="7332" target="_blank" rel="noreferrer noopener">list</a>.</p>



<pre class="EnlighterJSRAW" data-enlighter-language="python" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">(venv) $ cat office_details.py
import scrapy


class OfficeDetailsSpider(scrapy.Spider):
    name = 'office_details'
    start_urls = ['https://directory.ntschools.net/#/offices']

    def parse(self, response):
        pass
</pre>



<p class="wp-block-paragraph">Now we are going to send the request for getting the JSON object. For that, we can write the code inside the <code>parse</code> function.</p>



<pre class="EnlighterJSRAW" data-enlighter-language="python" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">    def parse(self, response):
        base_url = 'https://directory.ntschools.net/api/Office/GetOfficeList'
        headers = {
            'Accept': 'application/json',
            'Accept-Encoding': 'gzip, deflate, br',
            'Accept-Language': 'en-GB,en-US;q=0.9,en;q=0.8',
            'Cache-Control': 'no-cache',
            'Connection': 'keep-alive',
            'Host': 'directory.ntschools.net',
            'Pragma': 'no-cache',
            'Referer': 'https://directory.ntschools.net/',
            'Sec-Fetch-Dest': 'empty',
            'Sec-Fetch-Mode': 'cors',
            'Sec-Fetch-Site': 'same-origin',
            'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36',
            'X-Requested-With': 'Fetch'
            }
        yield scrapy.Request(url=base_url, callback=self.parse_api, headers=headers)
</pre>



<p class="wp-block-paragraph">So by using <code>yield</code>, the website containing the JSON object, that has been parsed to the function named <code>parse_api</code>. </p>



<p class="wp-block-paragraph">So from the response received in the function <code>parse_api</code>, we can get the JSON object. Now we need to import the <a href="https://blog.finxter.com/how-to-check-json-package-version-in-python/" data-type="post" data-id="479141" target="_blank" rel="noreferrer noopener">JSON module</a> into our Python file for loading the JSON object into our python file.</p>



<pre class="EnlighterJSRAW" data-enlighter-language="python" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">import json

    def parse_api(self, response):
        datas = json.loads(response.body)
        for data in datas:
            print(data['name'])
            if data['offices'] is not []:
                for sub in data['offices']:
                    print(f'    {sub["name"]}')
</pre>



<p class="wp-block-paragraph">This will give an output as follows:</p>


<div class="wp-block-image">
<figure class="aligncenter size-full"><img loading="lazy" decoding="async" width="479" height="435" src="https://blog.finxter.com/wp-content/uploads/2022/10/image-3.png" alt="" class="wp-image-742230" srcset="https://blog.finxter.com/wp-content/uploads/2022/10/image-3.png 479w, https://blog.finxter.com/wp-content/uploads/2022/10/image-3-300x272.png 300w" sizes="auto, (max-width: 479px) 100vw, 479px" /></figure>
</div>


<p class="wp-block-paragraph">This output is the same as shown on the website.</p>


<div class="wp-block-image">
<figure class="aligncenter size-full"><img loading="lazy" decoding="async" width="400" height="740" src="https://blog.finxter.com/wp-content/uploads/2022/10/image-4.png" alt="" class="wp-image-742233" srcset="https://blog.finxter.com/wp-content/uploads/2022/10/image-4.png 400w, https://blog.finxter.com/wp-content/uploads/2022/10/image-4-162x300.png 162w" sizes="auto, (max-width: 400px) 100vw, 400px" /></figure>
</div>


<pre class="EnlighterJSRAW" data-enlighter-language="python" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">import json

    def parse_api(self, response):
        datas = json.loads(response.body)
        print(datas[0])
</pre>



<p class="wp-block-paragraph">The output of the above code will be as follows:</p>


<div class="wp-block-image">
<figure class="aligncenter size-large"><img loading="lazy" decoding="async" width="1024" height="68" src="https://blog.finxter.com/wp-content/uploads/2022/10/image-5-1024x68.png" alt="" class="wp-image-742240" srcset="https://blog.finxter.com/wp-content/uploads/2022/10/image-5-1024x68.png 1024w, https://blog.finxter.com/wp-content/uploads/2022/10/image-5-300x20.png 300w, https://blog.finxter.com/wp-content/uploads/2022/10/image-5-768x51.png 768w, https://blog.finxter.com/wp-content/uploads/2022/10/image-5-1536x102.png 1536w, https://blog.finxter.com/wp-content/uploads/2022/10/image-5.png 1600w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>
</div>


<p class="wp-block-paragraph">If we look into the output, the postal and physical addresses are missing in this data. The office details page contained that information. </p>



<p class="wp-block-paragraph">When we click on the `<strong>Agency Services</strong>` new page is loaded, <a href="https://directory.ntschools.net/#/offices/details/asdiv" target="_blank" rel="noreferrer noopener">https://directory. ntschools.net/#/offices/details/asdiv</a>. But if we want to parse into the office details, we need to identify the code `<strong>asdiv</strong>`. </p>



<p class="wp-block-paragraph">But this code is included in the previous JSON object with the key name `<strong>itSchoolCode</strong>`. So we are going to modify our script to get that details.</p>



<pre class="EnlighterJSRAW" data-enlighter-language="python" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">    def parse_api(self, response):
        datas = json.loads(response.body)
        for data in datas:  
            print(f"{data['name']} -> {data['itSchoolCode']}")

            if data['offices'] is not []:
                for sub in data['offices']:
                    print(f"    {sub['name']} -> {sub['itSchoolCode']}")
</pre>



<p class="wp-block-paragraph">The output of this script is as follows:</p>


<div class="wp-block-image">
<figure class="aligncenter size-full"><img loading="lazy" decoding="async" width="644" height="420" src="https://blog.finxter.com/wp-content/uploads/2022/10/image-6.png" alt="" class="wp-image-742248" srcset="https://blog.finxter.com/wp-content/uploads/2022/10/image-6.png 644w, https://blog.finxter.com/wp-content/uploads/2022/10/image-6-300x196.png 300w" sizes="auto, (max-width: 644px) 100vw, 644px" /></figure>
</div>


<h2 class="wp-block-heading">Extract the Address From the Details Page</h2>



<p class="wp-block-paragraph">Now we need to fetch the URL details of the JSON object as we previously searched in the network tab. </p>



<p class="wp-block-paragraph">When we loaded the office details page, additional items were generated in the network tab, as seen in the following picture:</p>


<div class="wp-block-image">
<figure class="aligncenter size-large"><img loading="lazy" decoding="async" width="1024" height="506" src="https://blog.finxter.com/wp-content/uploads/2022/10/image-3-1024x506.jpeg" alt="" class="wp-image-742256" srcset="https://blog.finxter.com/wp-content/uploads/2022/10/image-3-1024x506.jpeg 1024w, https://blog.finxter.com/wp-content/uploads/2022/10/image-3-300x148.jpeg 300w, https://blog.finxter.com/wp-content/uploads/2022/10/image-3-768x380.jpeg 768w, https://blog.finxter.com/wp-content/uploads/2022/10/image-3-1536x759.jpeg 1536w, https://blog.finxter.com/wp-content/uploads/2022/10/image-3.jpeg 1600w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>
</div>


<p class="wp-block-paragraph"><code>GetOfficeDetails?param='asdiv'</code></p>


<div class="wp-block-image">
<figure class="aligncenter size-full"><img loading="lazy" decoding="async" width="829" height="375" src="https://blog.finxter.com/wp-content/uploads/2022/10/image-7.png" alt="" class="wp-image-742261" srcset="https://blog.finxter.com/wp-content/uploads/2022/10/image-7.png 829w, https://blog.finxter.com/wp-content/uploads/2022/10/image-7-300x136.png 300w, https://blog.finxter.com/wp-content/uploads/2022/10/image-7-768x347.png 768w" sizes="auto, (max-width: 829px) 100vw, 829px" /></figure>
</div>


<p class="wp-block-paragraph">Respose tab of <code>GetOfficeDetails?param='asdiv'</code></p>


<div class="wp-block-image">
<figure class="aligncenter size-full"><img loading="lazy" decoding="async" width="807" height="184" src="https://blog.finxter.com/wp-content/uploads/2022/10/image-8.png" alt="" class="wp-image-742264" srcset="https://blog.finxter.com/wp-content/uploads/2022/10/image-8.png 807w, https://blog.finxter.com/wp-content/uploads/2022/10/image-8-300x68.png 300w, https://blog.finxter.com/wp-content/uploads/2022/10/image-8-768x175.png 768w" sizes="auto, (max-width: 807px) 100vw, 807px" /></figure>
</div>


<p class="wp-block-paragraph">Request URL for JSON object shown in the previous picture</p>



<p class="wp-block-paragraph">Now we know the extension code for each office. Using this information, we can make another request to get the details of the new page. Another function with the name `<code>parse_office</code>`; is used for this.</p>



<p class="wp-block-paragraph">Before going to function <code>parse_office</code>, some modifications to make in the function <code>parse_api</code> by adding metadata as a <a href="https://blog.finxter.com/python-dictionary/" data-type="post" data-id="5232" target="_blank" rel="noreferrer noopener">dictionary</a>.</p>



<pre class="EnlighterJSRAW" data-enlighter-language="python" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">    def parse_api(self, response):
        url = 'https://directory.ntschools.net/api/Office/GetOfficeDetails?param='
        datas = json.loads(response.body)
        for data in datas: 
            code = data['itSchoolCode']
            parentDivision = {'parentDivision': data['parentDivision']}
            yield scrapy.Request(
                                url+code,
                                callback=self.parse_office,
                                meta=parentDivision,
                                headers=self.headers
                                )
            if data['offices'] is not []:
                for sub in data['offices']:
                    sub_code = sub['itSchoolCode']
                    parentDivision = {'parentDivision': sub['parentDivision']}
                    yield scrapy.Request(
                                        url+sub_code,
                                        callback=self.parse_office,
                                        meta=parentDivision,
                                        headers=self.headers
                                        )
</pre>



<p class="wp-block-paragraph">Now we can collect all the details by writing a function name parse_office.</p>



<pre class="EnlighterJSRAW" data-enlighter-language="python" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">    def parse_office(self, response):
        parentDivision = response.meta['parentDivision']
        office_detail = json.loads(response.body)
        print({
            'OfficeName': office_detail['name'],
            'PhysicalAddress': office_detail['physicalAddress']['displayAddress'],
            'PostalAddress': office_detail['postalAddress']['displayAddress'],
            'TelephoneNumber': office_detail['telephoneNumber'],
            'FaxNumber': office_detail['facsimileTelephoneNumber'],
            'Email': office_detail['mail'],
            'Website': office_detail['uri'],
            'ParentDivision': parentDivision,
            })
</pre>



<p class="wp-block-paragraph">Let’s see the total script developed until now:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">import scrapy
import json


class OfficeDetailsSpider(scrapy.Spider):
    name = 'office_details'
    start_urls = ['https://directory.ntschools.net/#/offices']
    headers = {
            'Accept': 'application/json',
            'Accept-Encoding': 'gzip, deflate, br',
            'Accept-Language': 'en-GB,en-US;q=0.9,en;q=0.8',
            'Cache-Control': 'no-cache',
            'Connection': 'keep-alive',
            'Host': 'directory.ntschools.net',
            'Pragma': 'no-cache',
            'Referer': 'https://directory.ntschools.net/',
            'Sec-Fetch-Dest': 'empty',
            'Sec-Fetch-Mode': 'cors',
            'Sec-Fetch-Site': 'same-origin',
            'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36',
            'X-Requested-With': 'Fetch'
            }


    def parse(self, response):
        base_url = 'https://directory.ntschools.net/api/Office/GetOfficeList'
        yield scrapy.Request(url=base_url, callback=self.parse_api, headers=self.headers)


    def parse_api(self, response):
        url = 'https://directory.ntschools.net/api/Office/GetOfficeDetails?param='
        datas = json.loads(response.body)
        for data in datas: 
            code = data['itSchoolCode']
            parentDivision = {'parentDivision': data['parentDivision']}
            yield scrapy.Request(
                                url+code,
                                callback=self.parse_office,
                                meta=parentDivision,
                                headers=self.headers
                                )
            if data['offices'] is not []:
                for sub in data['offices']:
                    sub_code = sub['itSchoolCode']
                    parentDivision = {'parentDivision': sub['parentDivision']}
                    yield scrapy.Request(
                                        url+sub_code,
                                        callback=self.parse_office,
                                        meta=parentDivision,
                                        headers=self.headers
                                        )


    def parse_office(self, response):
        parentDivision = response.meta['parentDivision']
        office_detail = json.loads(response.body)
        print({
            'OfficeName': office_detail['name'],
            'PhysicalAddress': office_detail['physicalAddress']['displayAddress'],
            'PostalAddress': office_detail['postalAddress']['displayAddress'],
            'TelephoneNumber': office_detail['telephoneNumber'],
            'FaxNumber': office_detail['facsimileTelephoneNumber'],
            'Email': office_detail['mail'],
            'Website': office_detail['uri'],
            'ParentDivision': parentDivision,
            })
</pre>



<p class="wp-block-paragraph">This script will generate an output as shown in the following picture:</p>


<div class="wp-block-image">
<figure class="aligncenter size-large"><img loading="lazy" decoding="async" width="1024" height="280" src="https://blog.finxter.com/wp-content/uploads/2022/10/image-9-1024x280.png" alt="" class="wp-image-742273" srcset="https://blog.finxter.com/wp-content/uploads/2022/10/image-9-1024x280.png 1024w, https://blog.finxter.com/wp-content/uploads/2022/10/image-9-300x82.png 300w, https://blog.finxter.com/wp-content/uploads/2022/10/image-9-768x210.png 768w, https://blog.finxter.com/wp-content/uploads/2022/10/image-9-1536x420.png 1536w, https://blog.finxter.com/wp-content/uploads/2022/10/image-9.png 1600w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>
</div>


<p class="wp-block-paragraph">It’s Great!</p>



<p class="wp-block-paragraph">Now we scrape all the details from the API available on the website.</p>



<h2 class="wp-block-heading">Extract Data by Running Python Script File Instead Crawling Scrapy from Terminal</h2>



<p class="wp-block-paragraph">Now we have to run the spider from the script itself and store the scraped data in a data frame and then print the data frame.</p>



<p class="wp-block-paragraph">For that, we need to <code>import CrawlerProcess</code> class from <code>scrapy.crawler</code>:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="python" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">from scrapy.crawler import CrawlerProcess

process = CrawlerProcess()
process.crawl(OfficeDetailsSpider)
process.start()
</pre>



<p class="wp-block-paragraph">Here we are assigning an object, process, to <code>CrawlerProcess()</code> then, we passed our spider class name through the <code>crawl()</code> method of <code>CrawlerProcess</code> class. To initiate the crawling we used another method, <code>start()</code>. </p>



<h2 class="wp-block-heading">Create Dataframe for the Office Details Scraped from the Website</h2>



<p class="wp-block-paragraph">The next challenge is to build a data frame with the details we scrape from the website. For that, we have to create a global list variable and dictionary in the <code>parse_office</code> function to be stored in this global variable.</p>



<pre class="EnlighterJSRAW" data-enlighter-language="python" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">Import pandas as pd

office_details = list()
...
...
...
    def parse_office(self, response):
        parentDivision = response.meta['parentDivision']
        office_data = json.loads(response.body)
        office_details.append(
            {
                'OfficeName': office_data['name'],
                'PhysicalAddress': office_data['physicalAddress']['displayAddress'],
                'PostalAddress': office_data['postalAddress']['displayAddress'],
                'TelephoneNumber': office_data['telephoneNumber'],
                'FaxNumber': office_data['facsimileTelephoneNumber'],
                'Email': office_data['mail'],
                'Website': office_data['uri'],
                'ParentDivision': parentDivision,
            }
        )
</pre>



<p class="wp-block-paragraph">We stored all the office details in a list and we can <a href="https://blog.finxter.com/how-to-convert-list-of-lists-to-a-pandas-dataframe/" data-type="post" data-id="7942" target="_blank" rel="noreferrer noopener">convert that list into a data frame</a> by using the following code.</p>



<pre class="EnlighterJSRAW" data-enlighter-language="python" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">df_office = pd.DataFrame(office_details)
print(df_office)
</pre>


<div class="wp-block-image">
<figure class="aligncenter size-large"><img loading="lazy" decoding="async" width="1024" height="379" src="https://blog.finxter.com/wp-content/uploads/2022/10/image-10-1024x379.png" alt="" class="wp-image-742292" srcset="https://blog.finxter.com/wp-content/uploads/2022/10/image-10-1024x379.png 1024w, https://blog.finxter.com/wp-content/uploads/2022/10/image-10-300x111.png 300w, https://blog.finxter.com/wp-content/uploads/2022/10/image-10-768x284.png 768w, https://blog.finxter.com/wp-content/uploads/2022/10/image-10-1536x568.png 1536w, https://blog.finxter.com/wp-content/uploads/2022/10/image-10.png 1600w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>
</div>


<p class="wp-block-paragraph">So all the challenges have been completed and now we can look into the whole script code with a proper structure:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="python" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">import scrapy
from scrapy.crawler import CrawlerProcess
import json
import pandas as pd

office_details = list()


class OfficeDetailsSpider(scrapy.Spider):
    name = 'office_details'
    start_urls = ['https://directory.ntschools.net/#/offices']
    headers = {
            'Accept': 'application/json',
            'Accept-Encoding': 'gzip, deflate, br',
            'Accept-Language': 'en-GB,en-US;q=0.9,en;q=0.8',
            'Cache-Control': 'no-cache',
            'Connection': 'keep-alive',
            'Host': 'directory.ntschools.net',
            'Pragma': 'no-cache',
            'Referer': 'https://directory.ntschools.net/',
            'Sec-Fetch-Dest': 'empty',
            'Sec-Fetch-Mode': 'cors',
            'Sec-Fetch-Site': 'same-origin',
            'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36',
            'X-Requested-With': 'Fetch'
            }


    def parse(self, response):
        base_url = 'https://directory.ntschools.net/api/Office/GetOfficeList'
        yield scrapy.Request(url=base_url, callback=self.parse_api, headers=self.headers)


    def parse_api(self, response):
        url = 'https://directory.ntschools.net/api/Office/GetOfficeDetails?param='
        datas = json.loads(response.body)
        for data in datas: 
            code = data['itSchoolCode']
            parentDivision = {'parentDivision': data['parentDivision']}
            yield scrapy.Request(
                                url+code,
                                callback=self.parse_office,
                                meta=parentDivision,
                                headers=self.headers
                                )
            if data['offices'] is not []:
                for sub in data['offices']:
                    sub_code = sub['itSchoolCode']
                    parentDivision = {'parentDivision': sub['parentDivision']}
                    yield scrapy.Request(
                                        url+sub_code,
                                        callback=self.parse_office,
                                        meta=parentDivision,
                                        headers=self.headers
                                        )


    def parse_office(self, response):
        parentDivision = response.meta['parentDivision']
        office_data = json.loads(response.body)
        office_details.append(
            {
                'OfficeName': office_data['name'],
                'PhysicalAddress': office_data['physicalAddress']['displayAddress'],
                'PostalAddress': office_data['postalAddress']['displayAddress'],
                'TelephoneNumber': office_data['telephoneNumber'],
                'FaxNumber': office_data['facsimileTelephoneNumber'],
                'Email': office_data['mail'],
                'Website': office_data['uri'],
                'ParentDivision': parentDivision,
            }
        )


def crawl():
    process = CrawlerProcess()
    process.crawl(OfficeDetailsSpider)
    process.start()


def main():
    crawl()
    df_office = pd.DataFrame(office_details)
    print(df_office)


if __name__ == '__main__':
    main()
</pre>
<p>The post <a href="https://blog.finxter.com/python-scrapy-scraping-dynamic-website-with-api-generated-content/">Python Scrapy &#8211; Scraping Dynamic Website with API-Generated Content</a> appeared first on <a href="https://blog.finxter.com">Be on the Right Side of Change</a>.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>How to Scrape the Details of 250 Top Rated Movies in Python</title>
		<link>https://blog.finxter.com/how-to-scrape-the-details-of-250-top-rated-movies-in-python/</link>
		
		<dc:creator><![CDATA[Anooj Rajamohan]]></dc:creator>
		<pubDate>Thu, 22 Sep 2022 14:50:08 +0000</pubDate>
				<category><![CDATA[Python]]></category>
		<category><![CDATA[Web Development]]></category>
		<category><![CDATA[Web Scraping]]></category>
		<guid isPermaLink="false">https://blog.finxter.com/?p=706667</guid>

					<description><![CDATA[<p>This article indicates a way to scrape imdb.com/chart/top/, a website that contains 250 numbers of top-rated Movies. This article is solely for educational purposes. 👉 Recommended Tutorial: Web Scraping &#8211; Is It Legal? The tool used to extract data from a website is Scrapy, and the software system is UNIX operating system. Virtual Environment Set ... <a title="How to Scrape the Details of 250 Top Rated Movies in Python" class="read-more" href="https://blog.finxter.com/how-to-scrape-the-details-of-250-top-rated-movies-in-python/" aria-label="Read more about How to Scrape the Details of 250 Top Rated Movies in Python">Read more</a></p>
<p>The post <a href="https://blog.finxter.com/how-to-scrape-the-details-of-250-top-rated-movies-in-python/">How to Scrape the Details of 250 Top Rated Movies in Python</a> appeared first on <a href="https://blog.finxter.com">Be on the Right Side of Change</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">This article indicates a way to scrape<a href="https://www.imdb.com/chart/top/"> </a><a href="https://www.imdb.com/chart/top/" target="_blank" rel="noreferrer noopener">imdb.com/chart/top/</a>, a website that contains 250 numbers of top-rated Movies. </p>


<div class="wp-block-image">
<figure class="aligncenter size-full"><img loading="lazy" decoding="async" width="778" height="786" src="https://blog.finxter.com/wp-content/uploads/2022/09/image-88.png" alt="" class="wp-image-706800" srcset="https://blog.finxter.com/wp-content/uploads/2022/09/image-88.png 778w, https://blog.finxter.com/wp-content/uploads/2022/09/image-88-297x300.png 297w, https://blog.finxter.com/wp-content/uploads/2022/09/image-88-768x776.png 768w" sizes="auto, (max-width: 778px) 100vw, 778px" /><figcaption><em>Screenshot of IMDB Top Movie Charts</em></figcaption></figure>
</div>


<p class="wp-block-paragraph">This article is solely for educational purposes. </p>



<p class="has-base-background-color has-background wp-block-paragraph"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f449.png" alt="👉" class="wp-smiley" style="height: 1em; max-height: 1em;" /> <strong>Recommended Tutorial</strong>: <a href="https://blog.finxter.com/is-web-scraping-legal/" data-type="post" data-id="383048" target="_blank" rel="noreferrer noopener">Web Scraping &#8211; Is It Legal?</a></p>



<p class="wp-block-paragraph">The tool used to extract data from a website is <a rel="noreferrer noopener" href="https://blog.finxter.com/how-to-install-scrapy-on-pycharm/" data-type="post" data-id="35232" target="_blank">Scrapy</a>, and the software system is UNIX operating system.</p>



<p class="wp-block-paragraph"></p>



<h2 class="wp-block-heading">Virtual Environment Set Up</h2>



<p class="wp-block-paragraph">It is better to use a <a href="https://blog.finxter.com/python-virtual-environments-conda/" data-type="post" data-id="3388" target="_blank" rel="noreferrer noopener">virtual environment</a> for setting up the project. There are different methods to establish a virtual environment, and here we use the venv module of python for setting up our project environment.</p>



<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">$ python3 -m venv 'name_of_enviro'</pre>



<p class="wp-block-paragraph">Once created a virtual environment, it has to be used as a project environment, using the subsequent command.</p>



<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">$ source 'name_of_enviro'/bin/activate</pre>



<p class="wp-block-paragraph">After activating the virtual environment, the prompt can show the virtual environment name as follows (I am using <code>`venv`</code> as the name of the virtual environment.</p>



<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">(venv)$</pre>



<h2 class="wp-block-heading">&nbsp;Scrapy Installation</h2>



<p class="wp-block-paragraph">&nbsp;This command will <a href="https://blog.finxter.com/how-to-install-scrapy-on-pycharm/" data-type="post" data-id="35232" target="_blank" rel="noreferrer noopener">install Scrapy</a> in the virtual environment.</p>



<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">(venv)$ pip install scrapy</pre>



<h2 class="wp-block-heading">Creating a Project in Scrapy</h2>



<p class="wp-block-paragraph">Before starting to extract, we need to set up a new Scrapy Project using a directory name to store all scrapy codes and run.</p>



<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">(venv)$ scrapy startproject top250Movies</pre>



<p class="wp-block-paragraph">The above command creates a `<code>top250Movies</code>` directory with the following files and directories.</p>



<pre class="wp-block-preformatted"><code>top250Movies
├── scrapy.cfg  # deploy configuration file
└── top250Movies  # project's Python module
    ├── __init__.py
    ├── items.py
    ├── middlewares.py
    ├── pipelines.py
    ├── settings.py
    └── spiders   # a directory for running the codes
        └── __init__.py
</code></pre>



<p class="wp-block-paragraph"></p>



<p class="wp-block-paragraph">After starting a new project, always move to the project directory. </p>



<p class="wp-block-paragraph">Our project directory is named `<code>top250Movies</code>`, so we move into that directory and start writing our codes by creating a python file inside the `<code>spiders</code>` directory. </p>



<p class="wp-block-paragraph">The scrapy module can only run the project from within the project directory. Otherwise, it will generate an error.</p>



<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">(venv)$ cd top250Movies
(venv)$ top250Movies>
</pre>



<h2 class="wp-block-heading">Start the coding</h2>



<p class="wp-block-paragraph">Let’s create a python file inside the directory named spiders.</p>



<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">(venv)$ top250Movies>touch top250Movies/spiders/firstSpider.py</pre>



<p class="wp-block-paragraph">So we created our project file, and now we need to import the library and build a spider. Spiders are the place where we define the custom behavior for crawling and parsing pages for a particular site or a group of sites).</p>



<pre class="EnlighterJSRAW" data-enlighter-language="python" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">import scrapy


class FirstSpider(scrapy.Spider):
    name = 'movies'
    start_urls = ['https://www.imdb.com/chart/top/']
    def parse(self, response):
        pass
</pre>



<p class="wp-block-paragraph">The above codes will explain the structure of a spider.</p>



<p class="wp-block-paragraph">The scraping goes through the following cycles:</p>



<ol class="wp-block-list"><li>Start by generating the initial Requests to crawl the first URL and specify a callback function to be called with the response downloaded from those requests.</li><li>In the callback function, we parse the response/URL and return item objects.</li><li>In callback functions, we parse the page contents, typically using Selectors (CSS Selector / Xpath Selector).</li><li>Finally, the items returned from the spider will persist in a database or be written to a file using Feed exports (<a href="https://blog.finxter.com/convert-csv-to-json-in-python/" data-type="post" data-id="423132" target="_blank" rel="noreferrer noopener">JSON</a>, <a href="https://blog.finxter.com/convert-csv-to-json-in-python/" data-type="post" data-id="423132" target="_blank" rel="noreferrer noopener">CSV</a>, etc.)</li></ol>



<p class="wp-block-paragraph">Here we need to follow the naming of variables and functions.</p>



<ul class="wp-block-list"><li>`<code>movies</code>` is a string that defines the name of this <a href="https://blog.finxter.com/how-to-run-a-scrapy-spider-from-a-python-script/" data-type="post" data-id="28124" target="_blank" rel="noreferrer noopener">spider</a>. The Scrapy is to use the spider name to locate the specified spider, so it must be unique.</li><li>`<code>start_urls</code>` contains a list of URLs on which the spider will begin to <a href="https://blog.finxter.com/using-scrapy-in-pycharm/" data-type="post" data-id="30486" target="_blank" rel="noreferrer noopener">crawl</a>.</li><li>`<code>parse(response)</code>` is the default callback used by Scrapy to process the response received when their requests are not specifying a callback. Then, the `<code>parse</code>` method will process the response and return the extracted data.</li></ul>



<p class="wp-block-paragraph">Before proceeding, we can look into the scrapy shell, which is very useful to identify the items we need to scrape and execute the same to ensure that we are getting the exact result as expected. </p>



<p class="wp-block-paragraph">For starting the scrapy shell, the following command will be used:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="python" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">(venv)[top250Movies]$ scrapy shell
>>> 
>>>fetch('https://www.imdb.com/chart/top/')
</pre>



<p class="wp-block-paragraph">The <code>fetch</code> command followed with URL will download the given URL using the Scrapy downloader and writes the contents to the response object.</p>



<p class="wp-block-paragraph">The response is an object that represents an <a href="https://blog.finxter.com/python-one-line-http-get/" data-type="post" data-id="12832" target="_blank" rel="noreferrer noopener">HTTP response</a>, which is usually downloaded and fed to the Spiders for processing.</p>



<p class="wp-block-paragraph">Response Parameters:</p>



<ul class="wp-block-list"><li><code>url</code> (string) – it will return the URL of the response.</li><li>status (integer) – it will return the HTTP status. If the output is 200, then it is good to go.</li><li><code>headers</code> (<a rel="noreferrer noopener" href="https://blog.finxter.com/python-dictionary/" data-type="post" data-id="5232" target="_blank">dictionary</a>) – it will return the headers. The dictionary values can be strings for single-valued headers and lists for multi-valued headers.</li><li><code>body</code> (<a rel="noreferrer noopener" href="https://blog.finxter.com/python-bytes-function/" data-type="post" data-id="17964" target="_blank">bytes</a>) – it will return the response body. To access the decoded text as a string, we use <code>response.text</code>.</li><li><code>view</code> (response) &#8212; it will open the response URL in a browser. Sometimes spiders see the URL pages differently from regular users. Hence this can be used to confirm what the spider sees and what we expect.</li></ul>


<div class="wp-block-image">
<figure class="aligncenter size-full"><img loading="lazy" decoding="async" width="768" height="424" src="https://blog.finxter.com/wp-content/uploads/2022/09/image-90.png" alt="" class="wp-image-711085" srcset="https://blog.finxter.com/wp-content/uploads/2022/09/image-90.png 768w, https://blog.finxter.com/wp-content/uploads/2022/09/image-90-300x166.png 300w" sizes="auto, (max-width: 768px) 100vw, 768px" /></figure>
</div>


<h2 class="wp-block-heading">Selecting Element Attributes</h2>



<p class="wp-block-paragraph">There are different ways to get a value of an attribute. Here we use simple <a href="https://blog.finxter.com/css-developer-income-and-opportunity/" data-type="post" data-id="193134" target="_blank" rel="noreferrer noopener">CSS</a> syntax:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">>>> response.css("td.titleColumn a::text").get()
'The Shawshank Redemption'</pre>



<p class="wp-block-paragraph">While inspecting the IMDb site for getting the Movie name, the particular CSS selector will be:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="python" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">&lt;td class="titleColumn">
&lt;a href="/title/tt0111161/" title="Frank Darabont (dir.), Tim Robbins, Morgan Freeman" >The Shawshank Redemption&lt;/a>
 	&lt;span class="secondaryInfo">(1994)&lt;/span>
&lt;/td>
</pre>



<p class="wp-block-paragraph">For getting all movie lists, instead of the <code>get()</code> method, we will use the <code>getall()</code> method, which will return all the Movie names as a <a href="https://blog.finxter.com/python-lists/" data-type="post" data-id="7332" target="_blank" rel="noreferrer noopener">list</a>. </p>



<p class="wp-block-paragraph">Similarly, we can use the following CSS selector for getting the movie release years which is inside the <code>&lt;span&gt;</code></p>



<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">>>> response.css("td.titleColumn span::text").getall()</pre>



<p class="wp-block-paragraph">In each code, we used `<code>::text</code>` after the <code>&lt;a&gt;</code> and <code>&lt;span&gt;</code> tags which will extract the text content under the each tags.</p>



<p class="wp-block-paragraph">Now we are good at writing code in our spider for crawling the website.</p>



<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">import scrapy
class FirstSpider(scrapy.Spider):
    name = 'movies'
    start_urls = ['https://www.imdb.com/chart/top/']
  	
def parse(self, response):
    movieContent = response.css("td.titleColumn")
    for item in movieContent:
        movieName = item.css("a::text").get()
        movieYear = item.css("span::text").get()
        movieDict = {'Movie Name': movieName,
                     'Release Year': movieYear}
        yield movieDict
    pass
</pre>



<p class="wp-block-paragraph"></p>



<p class="wp-block-paragraph">Here we used an additional variable `<code>movieContent</code>`, which stores the details of both <code>&lt;a></code> and <code>&lt;span></code> tags as list of selectors.</p>


<div class="wp-block-image">
<figure class="aligncenter size-full"><img loading="lazy" decoding="async" width="913" height="384" src="https://blog.finxter.com/wp-content/uploads/2022/09/image-91.png" alt="" class="wp-image-711090" srcset="https://blog.finxter.com/wp-content/uploads/2022/09/image-91.png 913w, https://blog.finxter.com/wp-content/uploads/2022/09/image-91-300x126.png 300w, https://blog.finxter.com/wp-content/uploads/2022/09/image-91-768x323.png 768w" sizes="auto, (max-width: 913px) 100vw, 913px" /></figure>
</div>


<p class="wp-block-paragraph">We can now iterate over all the <code>titleColumn</code> elements and put them together into a <a rel="noreferrer noopener" href="https://blog.finxter.com/python-dictionary/" data-type="post" data-id="5232" target="_blank">Python dictionary</a>. Scrapy spider generates dictionaries of data extracted from the page. Hence we use the <code><a rel="noreferrer noopener" href="https://blog.finxter.com/yield-keyword-in-python-a-simple-illustrated-guide/" data-type="post" data-id="14682" target="_blank">yield</a></code> keyword of Python in the callback as shown in the above code.</p>



<p class="wp-block-paragraph">To execute the spider without any error, we have to reach the project’s top-level directory and run:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">(venv)[~/top250Movies] $ scrapy crawl movies</pre>



<p class="wp-block-paragraph">This command will execute the spider with ‘<code>movies</code>’ as names, which we have added to the spider, which will send some requests for the URL domain for getting an output similar to this:</p>


<div class="wp-block-image">
<figure class="aligncenter size-full"><img loading="lazy" decoding="async" width="975" height="446" src="https://blog.finxter.com/wp-content/uploads/2022/09/image-92.png" alt="" class="wp-image-711101" srcset="https://blog.finxter.com/wp-content/uploads/2022/09/image-92.png 975w, https://blog.finxter.com/wp-content/uploads/2022/09/image-92-300x137.png 300w, https://blog.finxter.com/wp-content/uploads/2022/09/image-92-768x351.png 768w" sizes="auto, (max-width: 975px) 100vw, 975px" /></figure>
</div>


<p class="wp-block-paragraph">We have extracted 250 top movie names with the released year and logged the same into our screen. </p>



<p class="wp-block-paragraph">Now we will see how to store this output into a file. </p>



<p class="wp-block-paragraph">To store the output data into a file, we will use the parameter <code>-o</code> along with the filename (JSON/CSV).</p>



<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">(venv)[~/top250Movies] $ scrapy crawl movies -o movieList.csv</pre>



<p class="wp-block-paragraph">It will create a CSV file with the name `<code>movieList</code>` in our project directory.</p>



<h2 class="wp-block-heading">Summary</h2>



<p class="wp-block-paragraph">This article taught us how to install scrapy into a virtual environment.</p>



<p class="wp-block-paragraph">We learned how to start a project in scrapy and the basic structure of a scrapy project folder.</p>



<p class="wp-block-paragraph">We learned about the scrapy shell and the commands for getting the details from a URL</p>



<p class="wp-block-paragraph">Afterward, we learned how to write the spider for scraping the IMDb website using CSS selectors.</p>



<p class="wp-block-paragraph">Last but not least, we learned how to generate the output and how we can store the data into a JSON/CSV file.</p>



<p class="has-base-background-color has-background wp-block-paragraph"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f449.png" alt="👉" class="wp-smiley" style="height: 1em; max-height: 1em;" /> <strong>Recommended Tutorial</strong>: <a href="https://blog.finxter.com/python-developer-income-and-opportunity/" data-type="post" data-id="189354" target="_blank" rel="noreferrer noopener">Python Developer &#8212; Income and Opportunity</a></p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<p>The post <a href="https://blog.finxter.com/how-to-scrape-the-details-of-250-top-rated-movies-in-python/">How to Scrape the Details of 250 Top Rated Movies in Python</a> appeared first on <a href="https://blog.finxter.com">Be on the Right Side of Change</a>.</p>
]]></content:encoded>
					
		
		
			</item>
	</channel>
</rss>

<!--
Performance optimized by W3 Total Cache. Learn more: https://www.boldgrid.com/w3-total-cache/?utm_source=w3tc&utm_medium=footer_comment&utm_campaign=free_plugin

Page Caching using Disk: Enhanced 
Minified using Disk

Served from: blog.finxter.com @ 2026-10-20 23:54:13 by W3 Total Cache
-->