Daily News Scraper and Notifications - Part Two
Tags: python, heroku, tutorial, sms
Published: 2021-03-20 12:47:50

## Previously... 
To recap on  [part one](https://dev.to/ken_mwaura1/daily-news-scraper-and-sms-notifications-part-one-15m9) of this article. We built a daily news scraper using [newspaper3k]((https://newspaper.readthedocs.io/en/latest/)) and scraped two news platforms: [Business Daily Africa](https://www.businessdailyafrica.com/) and [Standard Media](https://www.standardmedia.co.ke/). We parsed the data and extracted the headline and url for the top 3 stories. We also made use of [Africas Talking ](https://africastalking.com/sms) amazing sms API and python sdk to send the headlines as notifications to a mobile number.

This article will cover url shortening for the article links and deployment to Heroku as well as scheduling the script to run in intervals.

## Prerequisites
- A [bitly](bitly.com) account
- A [Heroku]((https://www.heroku.com/home)) account
- Code from the [first article](https://dev.to/ken_mwaura1/daily-news-scraper-and-sms-notifications-part-one-15m9)
  OR check out the [github repository](https://github.com/KenMwaura1/daily-news-scraper)

## URL  Shortening
Previously we were just sending links as they are from the news source via text. However, this not only inefficient but expensive. Each text was exceeding the character limit for one text, thus the cost went up significantly for one text. For context , one text usually cost around *KES 0.8* per text, read more on [Africas Talking Pricing](https://africastalking.com/pricing).
Looking at my dashboard, the previous message cost went up to *KES 5.80* per text as shown below:

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kpkz1vca3odeaxczshjc.png)
This isn't a colossal amount, but we want the script to run every 30 mins or so throughout the day.
Factoring that in, it becomes a necessity to apply url shortening for our links.  Also it helps our clean up our text messages, this important in case you want to  scale this solution up to more people.

There are a good number of url shortening services, one of the best known is [bitly](https://bitly.com/). They have a generous free allocation for free accounts: about 1000 links per month. This should be enough for our use case, However if you need more upgrade options are available or creating multiple accounts. We could use the default [bitly](https://dev.bitly.com/) api and requests library, I came across an easier solution: [bitly shortener](https://pypi.org/project/bitlyshortener/). This library has a lot of cool features including allowing you to have a pool of tokens to concurrent make requests to, thereby significantly increasing the number of links you can shorten.

```python
    pip install bitlyshortener 
```

- Sign up for a new Bitly account at https://bitly.com/a/sign_up.
  An email address such as YourGmailUsername+SomeSuffix01@gmail.com should work.
- Under "Profile Settings", select "Generic Access Token".
  - Enter password and click Generate Token.

Once you've sorted all the above, edit your `.env `  and add the token as `bitly_token`, we then proceed to import the library and initialize the Shortener object and pass a list of tokens and `max_cache ` as arguments.

```python
    # news_scraper.py
  import bitlyshortener as bts
  """ 
  .... 
  other code here 
  """
  # Get the token(s) and create a shortener variable
  token = os.getenv('bitly_token')
  # create a variable to takes the token and returns a shortener object
  shortener = bts.Shortener(tokens=[token], max_cache_size=256)
```

We could create a separate function to create short urls, However integrating a separate function to the existing ` top_news` function and making sure the urls matched with the appropriate headlines proved a challenge. I decided to instead add it to the for loop.

```python
# Create a function to scrape the top 3 headlines from news sources
def top_news(url):
    # get top articles on standard standard
    news_source = newspaper.build(url, memoize_articles=False)
    top_articles = []

    for index in range(3):
        article = news_source.articles[index]
        article.download()
        article.parse()
        top_articles.append(article)
    for a in top_articles:
        # Shorten the long article urls using bitly shortener lib
        short_url = shortener.shorten_urls([a.url])
        message.append(a.title)
        # Short url is a list and we need to unpack it
        for url in short_url:
            message.append(url)
```
In the code above, we iterate through each link and  call `shorten_urls()`, since the function returns a list we unpack each url and append it the message list variable.

The shortener object provides a `usage()` function that returns a float about the usage of the current quota. In order to have visibility in our script we add a print statement and multiply it by 1000 to get a percentage.
(although this step is optional).

```python
    # news_scraper.py
    usage = shortener.usage()
    print(f"Current url quota usage: {usage * 1000}%")
```
## Deployment
For deployment of this project, I mainly chose Heroku as they provided an easy setup via Github also I already had an account with them. It is easy enough to choose any other cloud provider e,g. Digital Ocean, AWS, GCP etc.

After creating an account and logging in, I recommend you install the [heroku cli](https://devcenter.heroku.com/articles/heroku-cli) for easier time during deploying. Now lets begin deployment: just do a `heroku create --app <daily-news-scraper-ke>`. If you go on your app dashboard you'll see your new app.

We need to create a `runtime.txt` file to tell Heroku which version of python we want it run. This is important as bitlyshortener requires atleast version 3.7 and by default heroku uses version 3.6. I set mine to 3.9.2 to replicate my development environment.

```shell
  echo "python-3.9.2" > runtime.txt
```
We also need to specify [config vars](https://devcenter.heroku.com/articles/config-vars) that heroku will use during runtime. This similar how we've been
storing our credentials in a `.env` file. You could either set them via the heroku console in the browser or terminal
using the Heroku cli. Make sure you change the values to your actual credentials.

```shell
  heroku config:set bitly_token=bitly_token_here
  heroku config:set api_key=api_key_here
  heroku config:set username=Username_here
  heroku config:set mobile_number=2547XXXXXXXX

```

We now need to initialize a git repo and push the code on Heroku:
```shell
  git init
  heroku git:remote -a <heroku create --app daily-news-scraper-ke>
  git add .
  git commit -am 'Deploy news-scraper script'
  git push heroku master
```
Your app is now on Heroku, but it is not doing anything. Since this little script can't accept HTTP requests, going to <daily-news-scraper-ke>.herokuapp.com won't do anything.
But that should not be a problem. To have this script running 24/7 we need to use a simple Heroku add-on call "Heroku Scheduler". To install this add-on, click on the "Configure Add-ons" button on your app dashboard.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qkko2iug0w208c19cs9q.png)

Then, on the search bar, look for Heroku Scheduler:
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/uxm3y3mc772qu1btd6gh.png)

Click on the result, and click on "Provision"

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8kis77f7qdb8a7tcfpkg.png)
If you go back to your App dashboard, you'll see the add-on:

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wr3rvjt86dhyifv6zsoz.png)
Click on the "Heroku Scheduler" link to configure a job. Then click on "Create Job". Here select "30 minutes", and for run command select `python <news_scraper>.py`. Click on "Save job".

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/r0lh0lyzg817bx2wfzvr.png)

While everything we used so far on Heroku is free, the Heroku Scheduler will run the job on the $25/month instance, but prorated to the second. Since this script approximately takes 7 seconds to run, for this script to run every 30 minutes you should just have to spend 12 cents a month.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rn7vbprcfkdlv0ng8e8b.jpg)

## Conclusion
One key feature of the newspaper library is that it has [caching](https://newspaper.readthedocs.io/en/latest/user_guide/quickstart.html#article-caching) built-in.
This means once it crawls a site for headlines it caches the result, the next time it crawls if no new headlines are found it returns an empty list. This saves a lot of time and code prevent multiple texts with similar headlines.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gvea0nauernsg6bzslye.jpg)

I hope that you liked this post and that you learned things reading it.I truly believe that this kind of project is one of the best ways to learn new tools and concepts.

I have many other ideas, and I hope you will like them. Do not hesitate to share what other things you build with this snippet, possibilities are endless.

If you have any question or comments. Let me know in the comments, or on [Twitter](https://twitter.com/Ken_Mwaura1).
Happy Coding.