TL;DR version

from pandas_datareader import DataReader
import requests

USER_AGENT = {
    'User-Agent': ('Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko)'
                   ' Chrome/91.0.4472.124 Safari/537.36')
    }
sesh = requests.Session()
sesh.headers.update(USER_AGENT)

tw2330 = DataReader('2330.TW', data_source='yahoo', start='2000-01-01', session=sesh)
tw2330.head()
High Low Open Close Volume Adj Close
Date
2000-01-04 69.649033 68.475182 69.649033 69.649033 2.006623e+11 36.507061
2000-01-05 71.214043 68.866341 69.649033 71.214043 4.024668e+11 37.327374
2000-01-06 71.214043 69.649033 70.822884 69.649033 1.975457e+11 36.507061
2000-01-07 68.475182 66.518639 67.301331 67.692490 2.352703e+11 35.481518
2000-01-10 70.431351 68.475182 69.649033 70.040192 2.761717e+11 36.712093

Long version

Did the exception, thrown by pandas-datareader, shock you while you use it with Yahoo?

from pandas_datareader import DataReader
tw2330 = DataReader('2330.TW', data_source='yahoo', start='2020-01-01')
---------------------------------------------------------------------------
RemoteDataError                           Traceback (most recent call last)
<ipython-input-2-6a1152ea2d8a> in <module>()
----> 1 tw2330 = DataReader('2330.TW', data_source='yahoo', start='2020/01/01')

/usr/local/lib/python3.7/dist-packages/pandas/util/_decorators.py in wrapper(*args, **kwargs)
    197                 else:
    198                     kwargs[new_arg_name] = new_arg_value
--> 199             return func(*args, **kwargs)
    200 
    201         return cast(F, wrapper)

/usr/local/lib/python3.7/dist-packages/pandas_datareader/data.py in DataReader(name, data_source, start, end, retry_count, pause, session, api_key)
    382             retry_count=retry_count,
    383             pause=pause,
--> 384             session=session,
    385         ).read()
    386 

/usr/local/lib/python3.7/dist-packages/pandas_datareader/base.py in read(self)
    251         # If a single symbol, (e.g., 'GOOG')
    252         if isinstance(self.symbols, (string_types, int)):
--> 253             df = self._read_one_data(self.url, params=self._get_params(self.symbols))
    254         # Or multiple symbols, (e.g., ['GOOG', 'AAPL', 'MSFT'])
    255         elif isinstance(self.symbols, DataFrame):

/usr/local/lib/python3.7/dist-packages/pandas_datareader/yahoo/daily.py in _read_one_data(self, url, params)
    151         url = url.format(symbol)
    152 
--> 153         resp = self._get_response(url, params=params)
    154         ptrn = r"root\.App\.main = (.*?);\n}\(this\)\);"
    155         try:

/usr/local/lib/python3.7/dist-packages/pandas_datareader/base.py in _get_response(self, url, params, headers)
    179             msg += "\nResponse Text:\n{0}".format(last_response_text)
    180 
--> 181         raise RemoteDataError(msg)
    182 
    183     def _get_crumb(self, *args):

RemoteDataError: Unable to read URL: https://finance.yahoo.com/quote/2330.TW/history?period1=1577851200&period2=1626235199&interval=1d&frequency=1d&filter=history
Response Text:
b'<!DOCTYPE html>\n  <html lang="en-us"><head>\n  <meta http-equiv="content-type" content="text/html; charset=UTF-8">\n      <meta charset="utf-8">\n      <title>Yahoo</title>\n      <meta name="viewport" content="width=device-width,initial-scale=1,minimal-ui">\n      <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">\n      <style>\n  html {\n      height: 100%;\n  }\n  body {\n      background: #fafafc url(https://s.yimg.com/nn/img/sad-panda-201402200631.png) 50% 50%;\n      background-size: cover;\n      height: 100%;\n      text-align: center;\n      font: 300 18px "helvetica neue", helvetica, verdana, tahoma, arial, sans-serif;\n  }\n  table {\n      height: 100%;\n      width: 100%;\n      table-layout: fixed;\n      border-collapse: collapse;\n      border-spacing: 0;\n      border: none;\n  }\n  h1 {\n      font-size: 42px;\n      font-weight: 400;\n      color: #400090;\n  }\n  p {\n      color: #1A1A1A;\n  }\n  #message-1 {\n      font-weight: bold;\n      margin: 0;\n  }\n  #message-2 {\n      display: inline-block;\n      *display: inline;\n      zoom: 1;\n      max-width: 17em;\n      _width: 17em;\n  }\n      </style>\n  <script>\n    document.write(\'<img src="//geo.yahoo.com/b?s=1197757129&t=\'+new Date().getTime()+\'&src=aws&err_url=\'+encodeURIComponent(document.URL)+\'&err=%<pssc>&test=\'+encodeURIComponent(\'%<{Bucket}cqh[:200]>\')+\'" width="0px" height="0px"/>\');var beacon = new Image();beacon.src="//bcn.fp.yahoo.com/p?s=1197757129&t="+ne...

Don't panic. It seems Yahoo now checks the user agent in requests. So just add the user agent to header in requests.

Keep calm, and carry on.

Reference: _get_response without headers doesn't work (at least with 'yahoo' source #867